mirror of
https://github.com/chylex/Bark-Browser.git
synced 2025-09-15 23:32:11 +02:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
4c5a663f51
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -311,6 +311,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "slab_tree"
|
||||
version = "0.3.2"
|
||||
source = "git+https://github.com/jsinger67/slab-tree.git?branch=set_root_fix#75a8dfa3b1f96f76ebe09e2e988deeffe6db54b6"
|
||||
dependencies = [
|
||||
"snowflake",
|
||||
]
|
||||
|
@@ -21,4 +21,5 @@ slab_tree = "0.3.2"
|
||||
users = "0.11"
|
||||
|
||||
[patch.crates-io.slab_tree]
|
||||
path = "./lib/slab-tree"
|
||||
git = "https://github.com/jsinger67/slab-tree.git"
|
||||
branch = "set_root_fix"
|
||||
|
@@ -42,9 +42,3 @@ Run `docker build --output out .` to build a release binary into the `out/` fold
|
||||
This project exists 1) because I couldn't find any tree-based file manager I liked and 2) because I wanted to have fun writing Rust, and I don't really want to spend time reading and reviewing pull requests.
|
||||
|
||||
For now, issues are closed, and I'm not accepting any major contributions — especially ones related to the roadmap. If you have a small idea, issue, or pull request, feel free to start a [discussion](https://github.com/chylex/Bark-Browser/discussions).
|
||||
|
||||
# Dependencies
|
||||
|
||||
For a full list of dependencies, see [Cargo.toml](Cargo.toml).
|
||||
|
||||
This repository includes the sources of [slab-tree](https://github.com/iwburns/slab-tree) (by [iwburns](https://github.com/iwburns)) with a [bug fix for `set_root`](https://github.com/iwburns/slab-tree/pull/28) (by [jsinger67](https://github.com/jsinger67)) and additional modifications from me.
|
||||
|
2
lib/slab-tree/.gitignore
vendored
2
lib/slab-tree/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
/.idea/
|
||||
/target/
|
16
lib/slab-tree/Cargo.lock
generated
16
lib/slab-tree/Cargo.lock
generated
@@ -1,16 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "slab_tree"
|
||||
version = "0.3.2"
|
||||
dependencies = [
|
||||
"snowflake",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snowflake"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1"
|
@@ -1,15 +0,0 @@
|
||||
[package]
|
||||
name = "slab_tree"
|
||||
version = "0.3.2"
|
||||
authors = ["Ian <iwburns8@gmail.com>"]
|
||||
description = "A vec-backed tree structure with tree-specific generational indexes."
|
||||
documentation = "https://docs.rs/slab_tree"
|
||||
repository = "https://github.com/iwburns/slab-tree"
|
||||
readme = "README.md"
|
||||
keywords = ["tree", "slab", "slab-tree"]
|
||||
categories = ["data-structures"]
|
||||
license = "MIT"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
snowflake = "1.3.0"
|
@@ -1,69 +0,0 @@
|
||||
# slab_tree
|
||||
|
||||
[](https://travis-ci.org/iwburns/slab-tree)
|
||||
[](https://github.com/iwburns/slab-tree)
|
||||
|
||||
A vec-backed tree structure with tree-specific generational indexes.
|
||||
|
||||
## Overview
|
||||
|
||||
This library provides a `Tree<T>` struct which allows the creation and manipulation of in-memory trees.
|
||||
The tree itself is backed by a vector and the tree's node relationships are managed by tree-specific
|
||||
generational indexes called `NodeId`s (more below). In addition, "views" of tree nodes are handed out
|
||||
which are either immutable (`NodeRef`) or mutable (`NodeMut`) instead of handing out references
|
||||
directly. Most tree mutations are achieved by modifying `NodeMut`s instead of talking to the tree
|
||||
itself.
|
||||
|
||||
`Tree`s in this crate are "just" trees. They do not allow cycles, and they do not allow arbitrary
|
||||
graph structures to be created. Each node in the tree can have an arbitrary number of children, and
|
||||
there is no weight associated with edges between the nodes in the tree.
|
||||
|
||||
**Please Note:** this crate does not support comparison-based data insertion. In other words, this is
|
||||
not a binary search tree (or any other kind of search tree) crate. It is purely a crate for storing
|
||||
data in a hierarchical manner. The caller must know the structure that they wish to build and then use
|
||||
this crate to do so; this library will not make those structural decisions for you.
|
||||
|
||||
## Safety
|
||||
This crate uses `#![forbid(unsafe_code)]` to prevent any and all `unsafe` code usage.
|
||||
|
||||
## Example Usage
|
||||
```rust
|
||||
use slab_tree::*;
|
||||
|
||||
fn main() {
|
||||
|
||||
// "hello"
|
||||
// / \
|
||||
// "world" "trees"
|
||||
// |
|
||||
// "are"
|
||||
// |
|
||||
// "cool"
|
||||
|
||||
let mut tree = TreeBuilder::new().with_root("hello").build();
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let mut hello = tree.get_mut(root_id).unwrap();
|
||||
|
||||
hello.append("world");
|
||||
hello
|
||||
.append("trees")
|
||||
.append("are")
|
||||
.append("cool");
|
||||
}
|
||||
```
|
||||
|
||||
## `NodeId`s
|
||||
`NodeId`s are tree-specific generational indexes. Using generational indexes means that we can re-use
|
||||
space inside the tree (after nodes have been removed) without also having to re-use the same tree
|
||||
indexes which could potentially cause confusion and bugs. The "tree-specific" part means that indexes
|
||||
from one tree cannot be confused for indexes for another tree. This is because each index contains a
|
||||
process-unique-id which is shared by the tree from which that index originated.
|
||||
|
||||
## Project Goals
|
||||
* Allow caller control of as many allocations as possible (through pre-allocation)
|
||||
* Fast and Ergonomic Node insertion and removal
|
||||
* Arbitrary Tree structure creation and manipulation
|
||||
|
||||
## Non-Goals
|
||||
* Arbitrary _Graph_ structure creation and manipulation
|
||||
* Comparison-based node insertion of any kind
|
@@ -1,22 +0,0 @@
|
||||
///
|
||||
/// Describes all the possible ways to remove a Node from a Tree.
|
||||
///
|
||||
pub enum RemoveBehavior {
|
||||
///
|
||||
/// All children of the removed Node will be dropped from the Tree. All children (and all
|
||||
/// Nodes in each of their sub-trees) will no longer exist in the Tree after this operation.
|
||||
///
|
||||
/// This is slower than `OrphanChildren` but frees up space inside the Tree.
|
||||
///
|
||||
DropChildren,
|
||||
|
||||
///
|
||||
/// All children of the removed Node will be left in the Tree (still accessible via NodeIds).
|
||||
/// However, each child (and their sub-trees) will no longer be connected to the rest of the
|
||||
/// Nodes in the Tree.
|
||||
///
|
||||
/// Orphaned nodes will live in the Tree until they are manually removed or until the Tree is
|
||||
/// Dropped. This is faster than `DropChildren` but doesn't free up any space inside the Tree.
|
||||
///
|
||||
OrphanChildren,
|
||||
}
|
@@ -1,136 +0,0 @@
|
||||
use crate::node::Node;
|
||||
use crate::slab;
|
||||
use crate::NodeId;
|
||||
use snowflake::ProcessUniqueId;
|
||||
|
||||
///
|
||||
/// A wrapper around a Slab containing Node<T> values.
|
||||
///
|
||||
/// Groups a collection of Node<T>s with a process unique id.
|
||||
///
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct CoreTree<T> {
|
||||
id: ProcessUniqueId,
|
||||
slab: slab::Slab<Node<T>>,
|
||||
}
|
||||
|
||||
impl<T> CoreTree<T> {
|
||||
pub(crate) fn new(capacity: usize) -> CoreTree<T> {
|
||||
CoreTree {
|
||||
id: ProcessUniqueId::new(),
|
||||
slab: slab::Slab::new(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn capacity(&self) -> usize {
|
||||
self.slab.capacity()
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&mut self, data: T) -> NodeId {
|
||||
let key = self.slab.insert(Node::new(data));
|
||||
self.new_node_id(key)
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&mut self, node_id: NodeId) -> Option<T> {
|
||||
self.filter_by_tree_id(node_id)
|
||||
.and_then(|id| self.slab.remove(id.index))
|
||||
.map(|node| node.data)
|
||||
}
|
||||
|
||||
pub(crate) fn get(&self, node_id: NodeId) -> Option<&Node<T>> {
|
||||
self.filter_by_tree_id(node_id)
|
||||
.and_then(|id| self.slab.get(id.index))
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut(&mut self, node_id: NodeId) -> Option<&mut Node<T>> {
|
||||
self.filter_by_tree_id(node_id)
|
||||
.and_then(move |id| self.slab.get_mut(id.index))
|
||||
}
|
||||
|
||||
fn new_node_id(&self, index: slab::Index) -> NodeId {
|
||||
NodeId {
|
||||
tree_id: self.id,
|
||||
index,
|
||||
}
|
||||
}
|
||||
|
||||
fn filter_by_tree_id(&self, node_id: NodeId) -> Option<NodeId> {
|
||||
if node_id.tree_id != self.id {
|
||||
return None;
|
||||
}
|
||||
Some(node_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn capacity() {
|
||||
let capacity = 5;
|
||||
let tree = CoreTree::<i32>::new(capacity);
|
||||
assert_eq!(tree.capacity(), capacity);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert() {
|
||||
let mut tree = CoreTree::new(0);
|
||||
|
||||
let id = tree.insert(1);
|
||||
let id2 = tree.insert(3);
|
||||
|
||||
assert_eq!(tree.get(id).unwrap().data, 1);
|
||||
assert_eq!(tree.get(id2).unwrap().data, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove() {
|
||||
let mut tree = CoreTree::new(0);
|
||||
|
||||
let id = tree.insert(1);
|
||||
assert_eq!(tree.get(id).unwrap().data, 1);
|
||||
|
||||
let one = tree.remove(id);
|
||||
assert!(one.is_some());
|
||||
|
||||
let one = one.unwrap();
|
||||
assert_eq!(one, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
let mut tree = CoreTree::new(0);
|
||||
|
||||
let id = tree.insert(1);
|
||||
let id2 = tree.insert(3);
|
||||
|
||||
assert_eq!(tree.get(id).unwrap().data, 1);
|
||||
assert_eq!(tree.get(id2).unwrap().data, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_mut() {
|
||||
let mut tree = CoreTree::new(0);
|
||||
|
||||
let id = tree.insert(1);
|
||||
let id2 = tree.insert(3);
|
||||
|
||||
assert_eq!(tree.get_mut(id).unwrap().data, 1);
|
||||
assert_eq!(tree.get_mut(id2).unwrap().data, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_with_bad_id() {
|
||||
let mut tree = CoreTree::new(0);
|
||||
let tree2: CoreTree<i32> = CoreTree::new(0);
|
||||
|
||||
let mut id = tree.insert(1);
|
||||
id.tree_id = tree2.id; // oops, wrong tree id.
|
||||
|
||||
let result = tree.get(id);
|
||||
|
||||
assert!(result.is_none());
|
||||
}
|
||||
}
|
@@ -1,223 +0,0 @@
|
||||
use crate::node::*;
|
||||
use crate::tree::Tree;
|
||||
use crate::NodeId;
|
||||
|
||||
// todo: document this
|
||||
|
||||
pub struct Ancestors<'a, T> {
|
||||
node_id: Option<NodeId>,
|
||||
tree: &'a Tree<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> Ancestors<'a, T> {
|
||||
pub(crate) fn new(node_id: Option<NodeId>, tree: &'a Tree<T>) -> Ancestors<T> {
|
||||
Ancestors { node_id, tree }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Ancestors<'a, T> {
|
||||
type Item = NodeRef<'a, T>;
|
||||
|
||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
||||
self.node_id
|
||||
.take()
|
||||
.and_then(|node_id| self.tree.get_node_relatives(node_id).parent)
|
||||
.map(|id| {
|
||||
self.node_id = Some(id);
|
||||
NodeRef::new(id, self.tree)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// possibly re-name this, not sure how I feel about it
|
||||
pub struct NextSiblings<'a, T> {
|
||||
node_id: Option<NodeId>,
|
||||
tree: &'a Tree<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> NextSiblings<'a, T> {
|
||||
pub(crate) fn new(node_id: Option<NodeId>, tree: &'a Tree<T>) -> NextSiblings<T> {
|
||||
NextSiblings { node_id, tree }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for NextSiblings<'a, T> {
|
||||
type Item = NodeRef<'a, T>;
|
||||
|
||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
||||
self.node_id.take().map(|node_id| {
|
||||
self.node_id = self.tree.get_node_relatives(node_id).next_sibling;
|
||||
NodeRef::new(node_id, self.tree)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Depth-first pre-order iterator
|
||||
pub struct PreOrder<'a, T> {
|
||||
start: Option<NodeRef<'a, T>>,
|
||||
children: Vec<NextSiblings<'a, T>>,
|
||||
tree: &'a Tree<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> PreOrder<'a, T> {
|
||||
pub(crate) fn new(node: &NodeRef<'a, T>, tree: &'a Tree<T>) -> PreOrder<'a, T> {
|
||||
let children = vec![];
|
||||
let start = tree.get(node.node_id());
|
||||
PreOrder {
|
||||
start,
|
||||
children,
|
||||
tree,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for PreOrder<'a, T> {
|
||||
type Item = NodeRef<'a, T>;
|
||||
|
||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
||||
if let Some(node) = self.start.take() {
|
||||
let first_child_id = node.first_child().map(|child_ref| child_ref.node_id());
|
||||
self.children
|
||||
.push(NextSiblings::new(first_child_id, self.tree));
|
||||
Some(node)
|
||||
} else {
|
||||
while !self.children.is_empty() {
|
||||
if let Some(node_ref) = self.children.last_mut().and_then(Iterator::next) {
|
||||
if let Some(first_child) = node_ref.first_child() {
|
||||
self.children
|
||||
.push(NextSiblings::new(Some(first_child.node_id()), self.tree));
|
||||
}
|
||||
return Some(node_ref);
|
||||
}
|
||||
self.children.pop();
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Depth-first post-order iterator
|
||||
pub struct PostOrder<'a, T> {
|
||||
nodes: Vec<(NodeRef<'a, T>, NextSiblings<'a, T>)>,
|
||||
tree: &'a Tree<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> PostOrder<'a, T> {
|
||||
pub(crate) fn new(node: &NodeRef<'a, T>, tree: &'a Tree<T>) -> PostOrder<'a, T> {
|
||||
let node = tree
|
||||
.get(node.node_id())
|
||||
.expect("getting node of node ref id");
|
||||
let first_child_id = node.first_child().map(|first_child| first_child.node_id());
|
||||
let nodes = vec![(node, NextSiblings::new(first_child_id, tree))];
|
||||
PostOrder { nodes, tree }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for PostOrder<'a, T> {
|
||||
type Item = NodeRef<'a, T>;
|
||||
|
||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
||||
if let Some((node, mut children)) = self.nodes.pop() {
|
||||
if let Some(next) = children.next() {
|
||||
self.nodes.push((node, children));
|
||||
let mut node_id = next.node_id();
|
||||
loop {
|
||||
let node = self.tree.get(node_id).expect("getting node of node ref id");
|
||||
if let Some(first_child) = node.first_child() {
|
||||
node_id = first_child.node_id();
|
||||
let mut children = NextSiblings::new(Some(node_id), self.tree);
|
||||
assert!(children.next().is_some(), "skipping first child");
|
||||
self.nodes.push((node, children));
|
||||
} else {
|
||||
break Some(node);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Some(node)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Depth-first level-order iterator
|
||||
pub struct LevelOrder<'a, T> {
|
||||
start: NodeRef<'a, T>,
|
||||
levels: Vec<(NodeId, NextSiblings<'a, T>)>,
|
||||
tree: &'a Tree<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> LevelOrder<'a, T> {
|
||||
pub(crate) fn new(node: &NodeRef<'a, T>, tree: &'a Tree<T>) -> LevelOrder<'a, T> {
|
||||
let start = tree
|
||||
.get(node.node_id())
|
||||
.expect("getting node of node ref id");
|
||||
let levels = Vec::new();
|
||||
LevelOrder {
|
||||
start,
|
||||
levels,
|
||||
tree,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for LevelOrder<'a, T> {
|
||||
type Item = NodeRef<'a, T>;
|
||||
|
||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
||||
if self.levels.is_empty() {
|
||||
let first_child_id = self.start.first_child().map(|child| child.node_id());
|
||||
self.levels.push((
|
||||
self.start.node_id(),
|
||||
NextSiblings::new(first_child_id, self.tree),
|
||||
));
|
||||
let node = self
|
||||
.tree
|
||||
.get(self.start.node_id())
|
||||
.expect("getting node of existing node ref id");
|
||||
Some(node)
|
||||
} else {
|
||||
let mut on_level = self.levels.len();
|
||||
let next_level = on_level + 1;
|
||||
let mut level = on_level;
|
||||
while level > 0 {
|
||||
if let Some(node) = self.levels.last_mut().expect("non-empty levels").1.next() {
|
||||
if level >= on_level {
|
||||
return Some(node);
|
||||
} else {
|
||||
let first_child_id = node.first_child().map(|child| child.node_id());
|
||||
self.levels
|
||||
.push((node.node_id(), NextSiblings::new(first_child_id, self.tree)));
|
||||
level += 1;
|
||||
}
|
||||
} else {
|
||||
let (node_id, _) = self.levels.pop().expect("on level > 0");
|
||||
if let Some(next) = self.levels.last_mut().and_then(|level| level.1.next()) {
|
||||
let first_child_id = next.first_child().map(|child| child.node_id());
|
||||
self.levels
|
||||
.push((next.node_id(), NextSiblings::new(first_child_id, self.tree)));
|
||||
} else if level == 1 {
|
||||
if on_level < next_level {
|
||||
on_level += 1;
|
||||
let node = self
|
||||
.tree
|
||||
.get(node_id)
|
||||
.expect("getting node of existing node ref id");
|
||||
let first_child_id = node.first_child().map(|child| child.node_id());
|
||||
self.levels.push((
|
||||
node.node_id(),
|
||||
NextSiblings::new(first_child_id, self.tree),
|
||||
));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
level -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,96 +0,0 @@
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
//!
|
||||
//! # slab_tree
|
||||
//!
|
||||
//! [](https://travis-ci.org/iwburns/slab-tree)
|
||||
//! [](https://github.com/iwburns/slab-tree)
|
||||
//!
|
||||
//! A vec-backed tree structure with tree-specific generational indexes.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! This library provides a `Tree<T>` struct which allows the creation and manipulation of in-memory trees.
|
||||
//! The tree itself is backed by a vector and the tree's node relationships are managed by tree-specific
|
||||
//! generational indexes called `NodeId`s (more below). In addition, "views" of tree nodes are handed out
|
||||
//! which are either immutable (`NodeRef`) or mutable (`NodeMut`) instead of handing out references
|
||||
//! directly. Most tree mutations are achieved by modifying `NodeMut`s instead of talking to the tree
|
||||
//! itself.
|
||||
//!
|
||||
//! `Tree`s in this crate are "just" trees. They do not allow cycles, and they do not allow arbitrary
|
||||
//! graph structures to be created. Each node in the tree can have an arbitrary number of children, and
|
||||
//! there is no weight associated with edges between the nodes in the tree.
|
||||
//!
|
||||
//! **Please Note:** this crate does not support comparison-based data insertion. In other words, this is
|
||||
//! not a binary search tree (or any other kind of search tree) crate. It is purely a crate for storing
|
||||
//! data in a hierarchical manner. The caller must know the structure that they wish to build and then use
|
||||
//! this crate to do so; this library will not make those structural decisions for you.
|
||||
//!
|
||||
//! ## Safety
|
||||
//! This crate uses `#![forbid(unsafe_code)]` to prevent any and all `unsafe` code usage.
|
||||
//!
|
||||
//! ## Example Usage
|
||||
//! ```
|
||||
//! use slab_tree::*;
|
||||
//!
|
||||
//! // "hello"
|
||||
//! // / \
|
||||
//! // "world" "trees"
|
||||
//! // |
|
||||
//! // "are"
|
||||
//! // |
|
||||
//! // "cool"
|
||||
//!
|
||||
//! let mut tree = TreeBuilder::new().with_root("hello").build();
|
||||
//! let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
//! let mut hello = tree.get_mut(root_id).unwrap();
|
||||
//!
|
||||
//! hello.append("world");
|
||||
//! hello
|
||||
//! .append("trees")
|
||||
//! .append("are")
|
||||
//! .append("cool");
|
||||
//! ```
|
||||
//!
|
||||
//! ## `NodeId`s
|
||||
//! `NodeId`s are tree-specific generational indexes. Using generational indexes means that we can re-use
|
||||
//! space inside the tree (after nodes have been removed) without also having to re-use the same tree
|
||||
//! indexes which could potentially cause confusion and bugs. The "tree-specific" part means that indexes
|
||||
//! from one tree cannot be confused for indexes for another tree. This is because each index contains a
|
||||
//! process-unique-id which is shared by the tree from which that index originated.
|
||||
//!
|
||||
//! ## Project Goals
|
||||
//! * Allow caller control of as many allocations as possible (through pre-allocation)
|
||||
//! * Fast and Ergonomic Node insertion and removal
|
||||
//! * Arbitrary Tree structure creation and manipulation
|
||||
//!
|
||||
//! ## Non-Goals
|
||||
//! * Arbitrary _Graph_ structure creation and manipulation
|
||||
//! * Comparison-based node insertion of any kind
|
||||
//!
|
||||
|
||||
pub mod behaviors;
|
||||
mod core_tree;
|
||||
pub mod iter;
|
||||
pub mod node;
|
||||
mod slab;
|
||||
pub mod tree;
|
||||
|
||||
pub use crate::behaviors::RemoveBehavior;
|
||||
pub use crate::iter::Ancestors;
|
||||
pub use crate::iter::NextSiblings;
|
||||
pub use crate::node::NodeMut;
|
||||
pub use crate::node::NodeRef;
|
||||
pub use crate::tree::Tree;
|
||||
pub use crate::tree::TreeBuilder;
|
||||
use snowflake::ProcessUniqueId;
|
||||
|
||||
///
|
||||
/// An identifier used to differentiate between Nodes and tie
|
||||
/// them to a specific tree.
|
||||
///
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
||||
pub struct NodeId {
|
||||
tree_id: ProcessUniqueId,
|
||||
index: slab::Index,
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
mod bark_extensions;
|
||||
mod node_mut;
|
||||
mod node_ref;
|
||||
|
||||
pub use self::bark_extensions::*;
|
||||
pub use self::node_mut::NodeMut;
|
||||
pub use self::node_ref::NodeRef;
|
||||
|
||||
use crate::NodeId;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub(crate) struct Relatives {
|
||||
pub(crate) parent: Option<NodeId>,
|
||||
pub(crate) prev_sibling: Option<NodeId>,
|
||||
pub(crate) next_sibling: Option<NodeId>,
|
||||
pub(crate) first_child: Option<NodeId>,
|
||||
pub(crate) last_child: Option<NodeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Node<T> {
|
||||
pub(crate) data: T,
|
||||
pub(crate) relatives: Relatives,
|
||||
}
|
||||
|
||||
impl<T> Node<T> {
|
||||
pub(crate) fn new(data: T) -> Node<T> {
|
||||
Node {
|
||||
data,
|
||||
relatives: Relatives {
|
||||
parent: None,
|
||||
prev_sibling: None,
|
||||
next_sibling: None,
|
||||
first_child: None,
|
||||
last_child: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,79 +0,0 @@
|
||||
use crate::NodeId;
|
||||
use crate::NodeMut;
|
||||
use crate::NodeRef;
|
||||
|
||||
impl<'a, T> NodeRef<'a, T> {
|
||||
pub fn parent_id(&self) -> Option<NodeId> {
|
||||
self.parent().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn first_child_id(&self) -> Option<NodeId> {
|
||||
self.first_child().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn last_child_id(&self) -> Option<NodeId> {
|
||||
self.last_child().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn prev_sibling_id(&self) -> Option<NodeId> {
|
||||
self.prev_sibling().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn next_sibling_id(&self) -> Option<NodeId> {
|
||||
self.next_sibling().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn last_descendant_or_self(&self) -> NodeRef<'a, T> {
|
||||
let mut node = NodeRef::new(self.node_id(), self.tree);
|
||||
|
||||
while let Some(id) = node.get_self_as_node().relatives.last_child {
|
||||
node = NodeRef::new(id, self.tree);
|
||||
}
|
||||
|
||||
node
|
||||
}
|
||||
|
||||
pub fn above(&self) -> Option<NodeRef<T>> {
|
||||
if let Some(prev_sibling) = self.prev_sibling() {
|
||||
Some(prev_sibling.last_descendant_or_self())
|
||||
} else {
|
||||
self.parent()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn above_id(&self) -> Option<NodeId> {
|
||||
self.above().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn below(&self) -> Option<NodeRef<T>> {
|
||||
self.first_child()
|
||||
.or_else(|| self.next_sibling())
|
||||
.or_else(|| self.ancestors().find_map(|ancestor| ancestor.next_sibling_id()).map(|id| NodeRef::new(id, self.tree)))
|
||||
}
|
||||
|
||||
pub fn below_id(&self) -> Option<NodeId> {
|
||||
self.below().map(|node| node.node_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> NodeMut<'a, T> {
|
||||
pub fn parent_id(&mut self) -> Option<NodeId> {
|
||||
self.parent().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn first_child_id(&mut self) -> Option<NodeId> {
|
||||
self.first_child().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn last_child_id(&mut self) -> Option<NodeId> {
|
||||
self.last_child().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn prev_sibling_id(&mut self) -> Option<NodeId> {
|
||||
self.prev_sibling().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
pub fn next_sibling_id(&mut self) -> Option<NodeId> {
|
||||
self.next_sibling().map(|node| node.node_id())
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -1,381 +0,0 @@
|
||||
use crate::iter::Ancestors;
|
||||
use crate::iter::LevelOrder;
|
||||
use crate::iter::NextSiblings;
|
||||
use crate::iter::PostOrder;
|
||||
use crate::iter::PreOrder;
|
||||
use crate::node::Node;
|
||||
use crate::tree::Tree;
|
||||
use crate::NodeId;
|
||||
|
||||
///
|
||||
/// An immutable reference to a given `Node`'s data and its relatives.
|
||||
///
|
||||
pub struct NodeRef<'a, T> {
|
||||
pub(super) node_id: NodeId,
|
||||
pub(super) tree: &'a Tree<T>,
|
||||
}
|
||||
|
||||
impl<'a, T> NodeRef<'a, T> {
|
||||
pub(crate) fn new(node_id: NodeId, tree: &'a Tree<T>) -> NodeRef<T> {
|
||||
NodeRef { node_id, tree }
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the `NodeId` that identifies this `Node` in the tree.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
/// let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
/// let root = tree.root_mut().expect("root doesn't exist?");
|
||||
///
|
||||
/// let root_id_again = root.as_ref().node_id();
|
||||
///
|
||||
/// assert_eq!(root_id_again, root_id);
|
||||
/// ```
|
||||
///
|
||||
pub fn node_id(&self) -> NodeId {
|
||||
self.node_id
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a reference to the data contained by the given `Node`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
///
|
||||
/// let root = tree.root().expect("root doesn't exist?");
|
||||
///
|
||||
/// assert_eq!(root.data(), &1);
|
||||
/// ```
|
||||
///
|
||||
pub fn data(&self) -> &'a T {
|
||||
if let Some(node) = self.tree.get_node(self.node_id) {
|
||||
&node.data
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a `NodeRef` pointing to this `Node`'s parent. Returns a `Some`-value containing
|
||||
/// the `NodeRef` if this `Node` has a parent; otherwise returns a `None`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
///
|
||||
/// let root = tree.root().expect("root doesn't exist?");
|
||||
///
|
||||
/// assert!(root.parent().is_none());
|
||||
/// ```
|
||||
///
|
||||
pub fn parent(&self) -> Option<NodeRef<T>> {
|
||||
self.get_self_as_node()
|
||||
.relatives
|
||||
.parent
|
||||
.map(|id| NodeRef::new(id, self.tree))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a `NodeRef` pointing to this `Node`'s previous sibling. Returns a `Some`-value
|
||||
/// containing the `NodeRef` if this `Node` has a previous sibling; otherwise returns a `None`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
///
|
||||
/// let root = tree.root().expect("root doesn't exist?");
|
||||
///
|
||||
/// assert!(root.prev_sibling().is_none());
|
||||
/// ```
|
||||
///
|
||||
pub fn prev_sibling(&self) -> Option<NodeRef<T>> {
|
||||
self.get_self_as_node()
|
||||
.relatives
|
||||
.prev_sibling
|
||||
.map(|id| NodeRef::new(id, self.tree))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a `NodeRef` pointing to this `Node`'s next sibling. Returns a `Some`-value
|
||||
/// containing the `NodeRef` if this `Node` has a next sibling; otherwise returns a `None`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
///
|
||||
/// let root = tree.root().expect("root doesn't exist?");
|
||||
///
|
||||
/// assert!(root.next_sibling().is_none());
|
||||
/// ```
|
||||
///
|
||||
pub fn next_sibling(&self) -> Option<NodeRef<T>> {
|
||||
self.get_self_as_node()
|
||||
.relatives
|
||||
.next_sibling
|
||||
.map(|id| NodeRef::new(id, self.tree))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a `NodeRef` pointing to this `Node`'s first child. Returns a `Some`-value
|
||||
/// containing the `NodeRef` if this `Node` has a first child; otherwise returns a `None`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
///
|
||||
/// let root = tree.root().expect("root doesn't exist?");
|
||||
///
|
||||
/// assert!(root.first_child().is_none());
|
||||
/// ```
|
||||
///
|
||||
pub fn first_child(&self) -> Option<NodeRef<T>> {
|
||||
self.get_self_as_node()
|
||||
.relatives
|
||||
.first_child
|
||||
.map(|id| NodeRef::new(id, self.tree))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a `NodeRef` pointing to this `Node`'s last child. Returns a `Some`-value
|
||||
/// containing the `NodeRef` if this `Node` has a last child; otherwise returns a `None`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
///
|
||||
/// let root = tree.root().expect("root doesn't exist?");
|
||||
///
|
||||
/// assert!(root.last_child().is_none());
|
||||
/// ```
|
||||
///
|
||||
pub fn last_child(&self) -> Option<NodeRef<T>> {
|
||||
self.get_self_as_node()
|
||||
.relatives
|
||||
.last_child
|
||||
.map(|id| NodeRef::new(id, self.tree))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a `Iterator` over the given `Node`'s ancestors. Each call to `Iterator::next()`
|
||||
/// returns a `NodeRef` pointing to the current `Node`'s parent.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
///
|
||||
/// let leaf_id = tree.root_mut().expect("root doesn't exist?")
|
||||
/// .append(2)
|
||||
/// .append(3)
|
||||
/// .append(4)
|
||||
/// .node_id();
|
||||
///
|
||||
/// let leaf = tree.get(leaf_id).unwrap();
|
||||
///
|
||||
/// let values = [3, 2, 1];
|
||||
/// for (i, ancestor) in leaf.ancestors().enumerate() {
|
||||
/// assert_eq!(ancestor.data(), &values[i]);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn ancestors(&self) -> Ancestors<'a, T> {
|
||||
Ancestors::new(Some(self.node_id), self.tree)
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a `Iterator` over the given `Node`'s children. Each call to `Iterator::next()`
|
||||
/// returns a `NodeRef` pointing to the next child of the given `Node`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
///
|
||||
/// let mut root = tree.root_mut().expect("root doesn't exist?");
|
||||
/// root.append(2);
|
||||
/// root.append(3);
|
||||
/// root.append(4);
|
||||
///
|
||||
/// let root = root.as_ref();
|
||||
///
|
||||
/// let values = [2, 3, 4];
|
||||
/// for (i, child) in root.children().enumerate() {
|
||||
/// assert_eq!(child.data(), &values[i]);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
pub fn children(&self) -> NextSiblings<'a, T> {
|
||||
let first_child_id = self.tree.get_node_relatives(self.node_id).first_child;
|
||||
NextSiblings::new(first_child_id, self.tree)
|
||||
}
|
||||
|
||||
/// Depth-first pre-order traversal.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(0i64).build();
|
||||
/// let root_id = tree.root().unwrap().node_id();
|
||||
/// let one_id = tree.get_mut(root_id).unwrap().append(1).node_id();
|
||||
/// tree.get_mut(one_id).unwrap().append(2);
|
||||
/// tree.get_mut(one_id).unwrap().append(3);
|
||||
/// tree.get_mut(root_id).unwrap().append(4);
|
||||
/// let pre_order = tree.root().unwrap().traverse_pre_order()
|
||||
/// .map(|node_ref| node_ref.data().clone()).collect::<Vec<i64>>();
|
||||
/// assert_eq!(pre_order, vec![0, 1, 2, 3, 4]);
|
||||
/// ```
|
||||
pub fn traverse_pre_order(&self) -> PreOrder<'a, T> {
|
||||
PreOrder::new(self, self.tree)
|
||||
}
|
||||
|
||||
/// Depth-first post-order traversal.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(0i64).build();
|
||||
/// let root_id = tree.root().unwrap().node_id();
|
||||
/// let one_id = tree.get_mut(root_id).unwrap().append(1).node_id();
|
||||
/// tree.get_mut(one_id).unwrap().append(2);
|
||||
/// tree.get_mut(one_id).unwrap().append(3);
|
||||
/// tree.get_mut(root_id).unwrap().append(4);
|
||||
/// let post_order = tree.root().unwrap().traverse_post_order()
|
||||
/// .map(|node_ref| node_ref.data().clone()).collect::<Vec<i64>>();
|
||||
/// assert_eq!(post_order, vec![2, 3, 1, 4, 0]);
|
||||
/// ```
|
||||
pub fn traverse_post_order(&self) -> PostOrder<'a, T> {
|
||||
PostOrder::new(self, self.tree)
|
||||
}
|
||||
|
||||
/// Depth-first level-order traversal.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(0i64).build();
|
||||
/// let root_id = tree.root().unwrap().node_id();
|
||||
/// let one_id = tree.get_mut(root_id).unwrap().append(1).node_id();
|
||||
/// tree.get_mut(one_id).unwrap().append(2);
|
||||
/// tree.get_mut(one_id).unwrap().append(3);
|
||||
/// tree.get_mut(root_id).unwrap().append(4);
|
||||
/// let level_order = tree.root().unwrap().traverse_level_order()
|
||||
/// .map(|node_ref| node_ref.data().clone()).collect::<Vec<i64>>();
|
||||
/// assert_eq!(level_order, vec![0, 1, 4, 2, 3]);
|
||||
/// ```
|
||||
pub fn traverse_level_order(&self) -> LevelOrder<'a, T> {
|
||||
LevelOrder::new(self, self.tree)
|
||||
}
|
||||
|
||||
pub(super) fn get_self_as_node(&self) -> &Node<T> {
|
||||
if let Some(node) = self.tree.get_node(self.node_id) {
|
||||
&node
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
#[cfg(test)]
|
||||
mod node_ref_tests {
|
||||
use crate::tree::Tree;
|
||||
|
||||
#[test]
|
||||
fn data() {
|
||||
let mut tree = Tree::new();
|
||||
tree.set_root(1);
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root_ref = tree.get(root_id).unwrap();
|
||||
assert_eq!(root_ref.data(), &1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parent() {
|
||||
let mut tree = Tree::new();
|
||||
tree.set_root(1);
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root_ref = tree.get(root_id).unwrap();
|
||||
assert!(root_ref.parent().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prev_sibling() {
|
||||
let mut tree = Tree::new();
|
||||
tree.set_root(1);
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root_ref = tree.get(root_id).unwrap();
|
||||
assert!(root_ref.prev_sibling().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_sibling() {
|
||||
let mut tree = Tree::new();
|
||||
tree.set_root(1);
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root_ref = tree.get(root_id).unwrap();
|
||||
assert!(root_ref.next_sibling().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_child() {
|
||||
let mut tree = Tree::new();
|
||||
tree.set_root(1);
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root_ref = tree.get(root_id).unwrap();
|
||||
assert!(root_ref.first_child().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last_child() {
|
||||
let mut tree = Tree::new();
|
||||
tree.set_root(1);
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root_ref = tree.get(root_id).unwrap();
|
||||
assert!(root_ref.last_child().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ancestors() {
|
||||
let mut tree = Tree::new();
|
||||
tree.set_root(1);
|
||||
|
||||
let mut root_mut = tree.root_mut().expect("root doesn't exist");
|
||||
let node_id = root_mut.append(2).append(3).append(4).append(5).node_id();
|
||||
|
||||
let values = [4, 3, 2, 1];
|
||||
|
||||
let bottom_node = tree.get(node_id).unwrap();
|
||||
for (i, node_ref) in bottom_node.ancestors().enumerate() {
|
||||
assert_eq!(node_ref.data(), &values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn children() {
|
||||
let mut tree = Tree::new();
|
||||
tree.set_root(1);
|
||||
|
||||
let mut root = tree.root_mut().expect("root doesn't exist");
|
||||
root.append(2);
|
||||
root.append(3);
|
||||
root.append(4);
|
||||
root.append(5);
|
||||
|
||||
let values = [2, 3, 4, 5];
|
||||
let root = root.as_ref();
|
||||
|
||||
for (i, node_ref) in root.children().enumerate() {
|
||||
assert_eq!(node_ref.data(), &values[i]);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,497 +0,0 @@
|
||||
use std::mem;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
||||
pub(super) struct Index {
|
||||
index: usize,
|
||||
generation: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Slot<T> {
|
||||
Empty { next_free_slot: Option<usize> },
|
||||
Filled { item: T, generation: u64 },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(super) struct Slab<T> {
|
||||
data: Vec<Slot<T>>,
|
||||
first_free_slot: Option<usize>,
|
||||
generation: u64,
|
||||
}
|
||||
|
||||
impl<T> Slab<T> {
|
||||
pub(super) fn new(capacity: usize) -> Slab<T> {
|
||||
Slab {
|
||||
data: Vec::with_capacity(capacity),
|
||||
first_free_slot: None,
|
||||
generation: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn capacity(&self) -> usize {
|
||||
self.data.capacity()
|
||||
}
|
||||
|
||||
pub(super) fn insert(&mut self, item: T) -> Index {
|
||||
let new_slot = Slot::Filled {
|
||||
item,
|
||||
generation: self.generation,
|
||||
};
|
||||
|
||||
if let Some(index) = self.first_free_slot {
|
||||
match mem::replace(&mut self.data[index], new_slot) {
|
||||
Slot::Empty { next_free_slot } => {
|
||||
self.first_free_slot = next_free_slot;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
Index {
|
||||
index,
|
||||
generation: self.generation,
|
||||
}
|
||||
} else {
|
||||
self.data.push(new_slot);
|
||||
Index {
|
||||
index: self.data.len() - 1,
|
||||
generation: self.generation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn remove(&mut self, index: Index) -> Option<T> {
|
||||
if index.index >= self.data.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let slot = mem::replace(
|
||||
&mut self.data[index.index],
|
||||
Slot::Empty {
|
||||
next_free_slot: self.first_free_slot,
|
||||
},
|
||||
);
|
||||
|
||||
match slot {
|
||||
Slot::Filled { item, generation } => {
|
||||
if index.generation == generation {
|
||||
self.generation += 1;
|
||||
self.first_free_slot = Some(index.index);
|
||||
Some(item)
|
||||
} else {
|
||||
self.data[index.index] = Slot::Filled { item, generation };
|
||||
None
|
||||
}
|
||||
}
|
||||
s => {
|
||||
self.data[index.index] = s;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get(&self, index: Index) -> Option<&T> {
|
||||
self.data.get(index.index).and_then(|slot| match slot {
|
||||
Slot::Filled { item, generation } => {
|
||||
if index.generation == *generation {
|
||||
return Some(item);
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn get_mut(&mut self, index: Index) -> Option<&mut T> {
|
||||
self.data.get_mut(index.index).and_then(|slot| match slot {
|
||||
Slot::Filled { item, generation } => {
|
||||
if index.generation == *generation {
|
||||
return Some(item);
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn capacity() {
|
||||
let capacity = 5;
|
||||
let slab = Slab::<i32>::new(capacity);
|
||||
|
||||
assert_eq!(slab.capacity(), capacity);
|
||||
assert!(slab.first_free_slot.is_none());
|
||||
assert_eq!(slab.generation, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert() {
|
||||
let capacity = 2;
|
||||
let mut slab = Slab::new(capacity);
|
||||
|
||||
let six = slab.insert(6);
|
||||
|
||||
assert!(slab.first_free_slot.is_none());
|
||||
assert_eq!(slab.generation, 0);
|
||||
assert_eq!(slab.data.len(), 1);
|
||||
assert_eq!(slab.data.capacity(), capacity);
|
||||
|
||||
assert_eq!(six.generation, 0);
|
||||
assert_eq!(six.index, 0);
|
||||
|
||||
let seven = slab.insert(7);
|
||||
|
||||
assert!(slab.first_free_slot.is_none());
|
||||
assert_eq!(slab.generation, 0);
|
||||
assert_eq!(slab.data.len(), 2);
|
||||
assert_eq!(slab.data.capacity(), capacity);
|
||||
|
||||
assert_eq!(seven.generation, 0);
|
||||
assert_eq!(seven.index, 1);
|
||||
|
||||
let eight = slab.insert(8);
|
||||
|
||||
assert!(slab.first_free_slot.is_none());
|
||||
assert_eq!(slab.generation, 0);
|
||||
assert_eq!(slab.data.len(), 3);
|
||||
assert!(slab.data.capacity() >= capacity);
|
||||
|
||||
assert_eq!(eight.generation, 0);
|
||||
assert_eq!(eight.index, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_basic() {
|
||||
let mut slab = Slab::new(5);
|
||||
let _six = slab.insert(6);
|
||||
let seven = slab.insert(7);
|
||||
let _eight = slab.insert(8);
|
||||
// |6|7|8|
|
||||
|
||||
let seven_rem = slab.remove(seven);
|
||||
// |6|.|8|
|
||||
assert!(seven_rem.is_some());
|
||||
assert_eq!(seven_rem.unwrap(), 7);
|
||||
|
||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 1);
|
||||
assert_eq!(slab.generation, 1);
|
||||
|
||||
let six_slot = slab.data.get(0);
|
||||
assert!(six_slot.is_some());
|
||||
|
||||
match six_slot.unwrap() {
|
||||
Slot::Empty { .. } => {
|
||||
panic!("Slot should be filled after call to insert.");
|
||||
}
|
||||
Slot::Filled { item, generation } => {
|
||||
assert_eq!(item, &6);
|
||||
assert_eq!(generation, &0);
|
||||
}
|
||||
}
|
||||
|
||||
let seven_slot = slab.data.get(1);
|
||||
assert!(seven_slot.is_some());
|
||||
|
||||
match seven_slot.unwrap() {
|
||||
Slot::Empty { next_free_slot } => {
|
||||
assert!(next_free_slot.is_none());
|
||||
}
|
||||
Slot::Filled { .. } => {
|
||||
panic!("Slot should be empty after call to remove.");
|
||||
}
|
||||
}
|
||||
|
||||
let eight_slot = slab.data.get(2);
|
||||
assert!(eight_slot.is_some());
|
||||
|
||||
match eight_slot.unwrap() {
|
||||
Slot::Empty { .. } => {
|
||||
panic!("Slot should be filled after call to insert.");
|
||||
}
|
||||
Slot::Filled { item, generation } => {
|
||||
assert_eq!(item, &8);
|
||||
assert_eq!(generation, &0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_remove() {
|
||||
let mut slab = Slab::new(5);
|
||||
let _six = slab.insert(6);
|
||||
let seven = slab.insert(7);
|
||||
let _eight = slab.insert(8);
|
||||
// |6|7|8|
|
||||
|
||||
let seven_rem = slab.remove(seven);
|
||||
// |6|.|8|
|
||||
assert!(seven_rem.is_some());
|
||||
assert_eq!(seven_rem.unwrap(), 7);
|
||||
|
||||
let seven_again = slab.remove(seven);
|
||||
assert!(seven_again.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_multiple() {
|
||||
let mut slab = Slab::new(5);
|
||||
let _six = slab.insert(6);
|
||||
let seven = slab.insert(7);
|
||||
let eight = slab.insert(8);
|
||||
// |6|7|8|
|
||||
|
||||
let seven_rem = slab.remove(seven);
|
||||
// |6|.|8|
|
||||
assert!(seven_rem.is_some());
|
||||
assert_eq!(seven_rem.unwrap(), 7);
|
||||
|
||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 1);
|
||||
assert_eq!(slab.generation, 1);
|
||||
|
||||
let six_slot = slab.data.get(0);
|
||||
assert!(six_slot.is_some());
|
||||
|
||||
match six_slot.unwrap() {
|
||||
Slot::Empty { .. } => {
|
||||
panic!("Slot should be filled after call to insert.");
|
||||
}
|
||||
Slot::Filled { item, generation } => {
|
||||
assert_eq!(item, &6);
|
||||
assert_eq!(generation, &0);
|
||||
}
|
||||
}
|
||||
|
||||
let seven_slot = slab.data.get(1);
|
||||
assert!(seven_slot.is_some());
|
||||
|
||||
match seven_slot.unwrap() {
|
||||
Slot::Empty { next_free_slot } => {
|
||||
assert!(next_free_slot.is_none());
|
||||
}
|
||||
Slot::Filled { .. } => {
|
||||
panic!("Slot should be empty after call to remove.");
|
||||
}
|
||||
}
|
||||
|
||||
let eight_slot = slab.data.get(2);
|
||||
assert!(eight_slot.is_some());
|
||||
|
||||
match eight_slot.unwrap() {
|
||||
Slot::Empty { .. } => {
|
||||
panic!("Slot should be filled after call to insert.");
|
||||
}
|
||||
Slot::Filled { item, generation } => {
|
||||
assert_eq!(item, &8);
|
||||
assert_eq!(generation, &0);
|
||||
}
|
||||
}
|
||||
|
||||
let eight_rem = slab.remove(eight);
|
||||
// |6|.|.|
|
||||
assert!(eight_rem.is_some());
|
||||
assert_eq!(eight_rem.unwrap(), 8);
|
||||
|
||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 2);
|
||||
assert_eq!(slab.generation, 2);
|
||||
|
||||
let six_slot = slab.data.get(0);
|
||||
assert!(six_slot.is_some());
|
||||
|
||||
match six_slot.unwrap() {
|
||||
Slot::Empty { .. } => {
|
||||
panic!("Slot should be filled after call to insert.");
|
||||
}
|
||||
Slot::Filled { item, generation } => {
|
||||
assert_eq!(item, &6);
|
||||
assert_eq!(generation, &0);
|
||||
}
|
||||
}
|
||||
|
||||
let seven_slot = slab.data.get(1);
|
||||
assert!(seven_slot.is_some());
|
||||
|
||||
match seven_slot.unwrap() {
|
||||
Slot::Empty { next_free_slot } => {
|
||||
assert!(next_free_slot.is_none());
|
||||
}
|
||||
Slot::Filled { .. } => {
|
||||
panic!("Slot should be empty after call to remove.");
|
||||
}
|
||||
}
|
||||
|
||||
let eight_slot = slab.data.get(2);
|
||||
assert!(eight_slot.is_some());
|
||||
|
||||
match eight_slot.unwrap() {
|
||||
Slot::Empty { next_free_slot } => {
|
||||
assert!(next_free_slot.is_some());
|
||||
assert_eq!(next_free_slot.unwrap(), 1);
|
||||
}
|
||||
Slot::Filled { .. } => {
|
||||
panic!("Slot should be empty after call to remove.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_and_reinsert() {
|
||||
let mut slab = Slab::new(5);
|
||||
let _six = slab.insert(6);
|
||||
let seven = slab.insert(7);
|
||||
let eight = slab.insert(8);
|
||||
// |6|7|8|
|
||||
|
||||
let seven_rem = slab.remove(seven);
|
||||
// |6|.|8|
|
||||
assert!(seven_rem.is_some());
|
||||
assert_eq!(seven_rem.unwrap(), 7);
|
||||
|
||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 1);
|
||||
assert_eq!(slab.generation, 1);
|
||||
|
||||
let eight_rem = slab.remove(eight);
|
||||
// |6|.|.|
|
||||
assert!(eight_rem.is_some());
|
||||
assert_eq!(eight_rem.unwrap(), 8);
|
||||
|
||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 2);
|
||||
assert_eq!(slab.generation, 2);
|
||||
|
||||
let nine = slab.insert(9);
|
||||
// |6|.|9|
|
||||
assert_eq!(nine.index, 2);
|
||||
assert_eq!(nine.generation, 2);
|
||||
|
||||
let eight_again = slab.remove(eight);
|
||||
assert!(eight_again.is_none());
|
||||
|
||||
let six_slot = slab.data.get(0);
|
||||
assert!(six_slot.is_some());
|
||||
|
||||
match six_slot.unwrap() {
|
||||
Slot::Empty { .. } => {
|
||||
panic!("Slot should be filled after call to insert.");
|
||||
}
|
||||
Slot::Filled { item, generation } => {
|
||||
assert_eq!(item, &6);
|
||||
assert_eq!(generation, &0);
|
||||
}
|
||||
}
|
||||
|
||||
let seven_slot = slab.data.get(1);
|
||||
assert!(seven_slot.is_some());
|
||||
|
||||
match seven_slot.unwrap() {
|
||||
Slot::Empty { next_free_slot } => {
|
||||
assert!(next_free_slot.is_none());
|
||||
}
|
||||
Slot::Filled { .. } => {
|
||||
panic!("Slot should be empty after call to remove.");
|
||||
}
|
||||
}
|
||||
|
||||
let nine_slot = slab.data.get(2);
|
||||
assert!(nine_slot.is_some());
|
||||
|
||||
match nine_slot.unwrap() {
|
||||
Slot::Empty { .. } => {
|
||||
panic!("Slot should be filled after call to insert.");
|
||||
}
|
||||
Slot::Filled { item, generation } => {
|
||||
assert_eq!(item, &9);
|
||||
assert_eq!(generation, &2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_with_bad_index() {
|
||||
let mut slab = Slab::new(5);
|
||||
let _six = slab.insert(6);
|
||||
let _seven = slab.insert(7);
|
||||
let mut eight = slab.insert(8);
|
||||
// |0|1|2| index
|
||||
// |6|7|8| value
|
||||
|
||||
eight.index = 3; // oops, this should be 2
|
||||
|
||||
let eight_rem = slab.remove(eight);
|
||||
assert!(eight_rem.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
let mut slab = Slab::new(5);
|
||||
|
||||
let six = slab.insert(6);
|
||||
assert_eq!(six.index, 0);
|
||||
assert_eq!(six.generation, 0);
|
||||
|
||||
let seven = slab.insert(7);
|
||||
assert_eq!(seven.index, 1);
|
||||
assert_eq!(seven.generation, 0);
|
||||
|
||||
let six_ref = slab.get(six);
|
||||
assert!(six_ref.is_some());
|
||||
assert_eq!(six_ref.unwrap(), &6);
|
||||
|
||||
slab.remove(six);
|
||||
|
||||
let six_ref = slab.get(six);
|
||||
assert!(six_ref.is_none());
|
||||
|
||||
let eight = slab.insert(8);
|
||||
assert_eq!(eight.index, 0);
|
||||
assert_eq!(eight.generation, 1);
|
||||
|
||||
let eight_ref = slab.get(eight);
|
||||
assert!(eight_ref.is_some());
|
||||
assert_eq!(eight_ref.unwrap(), &8);
|
||||
|
||||
let six_ref = slab.get(six);
|
||||
assert!(six_ref.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_mut() {
|
||||
let mut slab = Slab::new(5);
|
||||
|
||||
let six = slab.insert(6);
|
||||
assert_eq!(six.index, 0);
|
||||
assert_eq!(six.generation, 0);
|
||||
|
||||
let seven = slab.insert(7);
|
||||
assert_eq!(seven.index, 1);
|
||||
assert_eq!(seven.generation, 0);
|
||||
|
||||
let six_mut = slab.get_mut(six);
|
||||
assert!(six_mut.is_some());
|
||||
|
||||
let six_mut = six_mut.unwrap();
|
||||
assert_eq!(six_mut, &mut 6);
|
||||
|
||||
*six_mut = 60;
|
||||
assert_eq!(six_mut, &mut 60);
|
||||
|
||||
slab.remove(six);
|
||||
|
||||
let six_ref = slab.get_mut(six);
|
||||
assert!(six_ref.is_none());
|
||||
|
||||
let eight = slab.insert(8);
|
||||
assert_eq!(eight.index, 0);
|
||||
assert_eq!(eight.generation, 1);
|
||||
|
||||
let eight_ref = slab.get_mut(eight);
|
||||
assert!(eight_ref.is_some());
|
||||
assert_eq!(eight_ref.unwrap(), &mut 8);
|
||||
|
||||
let six_ref = slab.get_mut(six);
|
||||
assert!(six_ref.is_none());
|
||||
}
|
||||
}
|
@@ -1,813 +0,0 @@
|
||||
use crate::behaviors::*;
|
||||
use crate::core_tree::CoreTree;
|
||||
use crate::node::*;
|
||||
use crate::NodeId;
|
||||
|
||||
///
|
||||
/// A `Tree` builder. Provides more control over how a `Tree` is created.
|
||||
///
|
||||
pub struct TreeBuilder<T> {
|
||||
root: Option<T>,
|
||||
capacity: Option<usize>,
|
||||
}
|
||||
|
||||
impl<T> Default for TreeBuilder<T> {
|
||||
fn default() -> Self {
|
||||
TreeBuilder::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TreeBuilder<T> {
|
||||
///
|
||||
/// Creates a new `TreeBuilder` with the default settings.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let _tree_builder = TreeBuilder::new();
|
||||
///
|
||||
/// # _tree_builder.with_root(1);
|
||||
/// ```
|
||||
///
|
||||
pub fn new() -> TreeBuilder<T> {
|
||||
TreeBuilder {
|
||||
root: None,
|
||||
capacity: None,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the root `Node` of the `TreeBuilder`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let _tree_builder = TreeBuilder::new().with_root(1);
|
||||
/// ```
|
||||
///
|
||||
pub fn with_root(self, root: T) -> TreeBuilder<T> {
|
||||
TreeBuilder {
|
||||
root: Some(root),
|
||||
capacity: self.capacity,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Sets the capacity of the `TreeBuilder`.
|
||||
///
|
||||
/// This can be used to pre-allocate space in the `Tree` if you know you'll be adding a large
|
||||
/// number of `Node`s to the `Tree`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let _tree_builder = TreeBuilder::new().with_capacity(10);
|
||||
///
|
||||
/// # _tree_builder.with_root(1);
|
||||
/// ```
|
||||
///
|
||||
pub fn with_capacity(self, capacity: usize) -> TreeBuilder<T> {
|
||||
TreeBuilder {
|
||||
root: self.root,
|
||||
capacity: Some(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Build a `Tree` based upon the current settings in the `TreeBuilder`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let _tree = TreeBuilder::new().with_root(1).with_capacity(10).build();
|
||||
/// ```
|
||||
///
|
||||
pub fn build(self) -> Tree<T> {
|
||||
let capacity = self.capacity.unwrap_or(0);
|
||||
let mut core_tree: CoreTree<T> = CoreTree::new(capacity);
|
||||
let root_id = self.root.map(|val| core_tree.insert(val));
|
||||
|
||||
Tree { root_id, core_tree }
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// A tree structure containing `Node`s.
|
||||
///
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Tree<T> {
|
||||
pub(crate) root_id: Option<NodeId>,
|
||||
pub(crate) core_tree: CoreTree<T>,
|
||||
}
|
||||
|
||||
impl<T> Tree<T> {
|
||||
///
|
||||
/// Creates a new `Tree` with a capacity of 0.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::Tree;
|
||||
///
|
||||
/// let tree: Tree<i32> = Tree::new();
|
||||
///
|
||||
/// # assert_eq!(tree.capacity(), 0);
|
||||
/// ```
|
||||
///
|
||||
pub fn new() -> Tree<T> {
|
||||
TreeBuilder::new().build()
|
||||
}
|
||||
|
||||
//todo: write test for this
|
||||
///
|
||||
/// Sets the "root" of the `Tree` to be `root`.
|
||||
///
|
||||
/// If there is already a "root" node in the `Tree`, that node is shifted down and the new
|
||||
/// one takes its place.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::Tree;
|
||||
///
|
||||
/// let mut tree = Tree::new();
|
||||
///
|
||||
/// let root_id = tree.set_root(1);
|
||||
///
|
||||
/// assert_eq!(tree.root_id().unwrap(), root_id);
|
||||
/// ```
|
||||
///
|
||||
pub fn set_root(&mut self, root: T) -> NodeId {
|
||||
let old_root_id = self.root_id.take();
|
||||
let new_root_id = self.core_tree.insert(root);
|
||||
|
||||
self.root_id = Some(new_root_id);
|
||||
|
||||
self.set_first_child(new_root_id, old_root_id);
|
||||
self.set_last_child(new_root_id, old_root_id);
|
||||
|
||||
if let Some(node_id) = old_root_id {
|
||||
self.set_parent(node_id, self.root_id);
|
||||
}
|
||||
|
||||
new_root_id
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the `Tree`'s current capacity. Capacity is defined as the number of times new
|
||||
/// `Node`s can be added to the `Tree` before it must allocate more memory.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::Tree;
|
||||
///
|
||||
/// let tree: Tree<i32> = Tree::new();
|
||||
///
|
||||
/// assert_eq!(tree.capacity(), 0);
|
||||
/// ```
|
||||
///
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.core_tree.capacity()
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the `NodeId` of the root node of the `Tree`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::Tree;
|
||||
///
|
||||
/// let mut tree = Tree::new();
|
||||
/// tree.set_root(1);
|
||||
///
|
||||
/// let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
///
|
||||
/// assert_eq!(tree.get(root_id).unwrap().data(), &1);
|
||||
/// ```
|
||||
///
|
||||
pub fn root_id(&self) -> Option<NodeId> {
|
||||
self.root_id
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a `NodeRef` pointing to the root `Node` of the `Tree`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::Tree;
|
||||
///
|
||||
/// let mut tree = Tree::new();
|
||||
/// tree.set_root(1);
|
||||
///
|
||||
/// let root = tree.root().expect("root doesn't exist?");
|
||||
///
|
||||
/// assert_eq!(root.data(), &1);
|
||||
/// ```
|
||||
///
|
||||
pub fn root(&self) -> Option<NodeRef<T>> {
|
||||
self.root_id.map(|id| self.new_node_ref(id))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a `NodeMut` pointing to the root `Node` of the `Tree`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::Tree;
|
||||
///
|
||||
/// let mut tree = Tree::new();
|
||||
/// tree.set_root(1);
|
||||
///
|
||||
/// let mut root = tree.root_mut().expect("root doesn't exist?");
|
||||
/// assert_eq!(root.data(), &mut 1);
|
||||
///
|
||||
/// *root.data() = 2;
|
||||
/// assert_eq!(root.data(), &mut 2);
|
||||
/// ```
|
||||
///
|
||||
pub fn root_mut(&mut self) -> Option<NodeMut<T>> {
|
||||
self.root_id.map(move |id| self.new_node_mut(id))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the `NodeRef` pointing to the `Node` that the given `NodeId` identifies. If the
|
||||
/// `NodeId` in question points to nothing (or belongs to a different `Tree`) a `None`-value
|
||||
/// will be returned; otherwise, a `Some`-value will be returned.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::Tree;
|
||||
///
|
||||
/// let mut tree = Tree::new();
|
||||
/// tree.set_root(1);
|
||||
/// let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
///
|
||||
/// let root = tree.get(root_id);
|
||||
/// assert!(root.is_some());
|
||||
///
|
||||
/// let root = root.unwrap();
|
||||
/// assert_eq!(root.data(), &1);
|
||||
/// ```
|
||||
///
|
||||
pub fn get(&self, node_id: NodeId) -> Option<NodeRef<T>> {
|
||||
let _ = self.core_tree.get(node_id)?;
|
||||
Some(self.new_node_ref(node_id))
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the `NodeMut` pointing to the `Node` that the given `NodeId` identifies. If the
|
||||
/// `NodeId` in question points to nothing (or belongs to a different `Tree`) a `None`-value
|
||||
/// will be returned; otherwise, a `Some`-value will be returned.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::Tree;
|
||||
///
|
||||
/// let mut tree = Tree::new();
|
||||
/// tree.set_root(1);
|
||||
/// let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
///
|
||||
/// let root = tree.get_mut(root_id);
|
||||
/// assert!(root.is_some());
|
||||
///
|
||||
/// let mut root = root.unwrap();
|
||||
///
|
||||
/// *root.data() = 2;
|
||||
/// assert_eq!(root.data(), &mut 2);
|
||||
/// ```
|
||||
///
|
||||
pub fn get_mut(&mut self, node_id: NodeId) -> Option<NodeMut<T>> {
|
||||
let _ = self.core_tree.get_mut(node_id)?;
|
||||
Some(self.new_node_mut(node_id))
|
||||
}
|
||||
|
||||
///
|
||||
/// Remove a `Node` by its `NodeId` and return the data that it contained.
|
||||
/// Returns a `Some`-value if the `Node` exists; returns a `None`-value otherwise.
|
||||
///
|
||||
/// Children of the removed `Node` can either be dropped with `DropChildren` or orphaned with
|
||||
/// `OrphanChildren`.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
/// use slab_tree::behaviors::RemoveBehavior::*;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
/// let two_id = {
|
||||
/// let mut root = tree.root_mut().expect("root doesn't exist?");
|
||||
/// let two_id = root.append(2).node_id();
|
||||
/// root.append(3);
|
||||
/// two_id
|
||||
/// };
|
||||
///
|
||||
/// let two = tree.remove(two_id, DropChildren);
|
||||
///
|
||||
/// assert!(two.is_some());
|
||||
/// assert_eq!(two.unwrap(), 2);
|
||||
///
|
||||
/// let root = tree.root().expect("root doesn't exist?");
|
||||
/// assert!(root.first_child().is_some());
|
||||
/// assert_eq!(root.first_child().unwrap().data(), &mut 3);
|
||||
///
|
||||
/// assert!(root.last_child().is_some());
|
||||
/// assert_eq!(root.last_child().unwrap().data(), &mut 3);
|
||||
/// ```
|
||||
///
|
||||
pub fn remove(&mut self, node_id: NodeId, behavior: RemoveBehavior) -> Option<T> {
|
||||
if let Some(node) = self.get_node(node_id) {
|
||||
let Relatives {
|
||||
parent,
|
||||
prev_sibling,
|
||||
next_sibling,
|
||||
..
|
||||
} = node.relatives;
|
||||
|
||||
let (is_first_child, is_last_child) = self.is_node_first_last_child(node_id);
|
||||
|
||||
if is_first_child {
|
||||
// parent first child = my next sibling
|
||||
self.set_first_child(parent.expect("parent must exist"), next_sibling);
|
||||
}
|
||||
if is_last_child {
|
||||
// parent last child = my prev sibling
|
||||
self.set_last_child(parent.expect("parent must exist"), prev_sibling);
|
||||
}
|
||||
if let Some(prev) = prev_sibling {
|
||||
self.set_next_sibling(prev, next_sibling);
|
||||
}
|
||||
if let Some(next) = next_sibling {
|
||||
self.set_prev_sibling(next, prev_sibling);
|
||||
}
|
||||
|
||||
match behavior {
|
||||
RemoveBehavior::DropChildren => self.drop_children(node_id),
|
||||
RemoveBehavior::OrphanChildren => self.orphan_children(node_id),
|
||||
};
|
||||
if self.root_id == Some(node_id) {
|
||||
self.root_id = None;
|
||||
}
|
||||
self.core_tree.remove(node_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_node(&self, node_id: NodeId) -> Option<&Node<T>> {
|
||||
self.core_tree.get(node_id)
|
||||
}
|
||||
|
||||
pub(crate) fn get_node_mut(&mut self, node_id: NodeId) -> Option<&mut Node<T>> {
|
||||
self.core_tree.get_mut(node_id)
|
||||
}
|
||||
|
||||
pub(crate) fn set_prev_siblings_next_sibling(
|
||||
&mut self,
|
||||
current_id: NodeId,
|
||||
next_sibling: Option<NodeId>,
|
||||
) {
|
||||
if let Some(prev_sibling_id) = self.get_node_prev_sibling_id(current_id) {
|
||||
self.set_next_sibling(prev_sibling_id, next_sibling);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_next_siblings_prev_sibling(
|
||||
&mut self,
|
||||
current_id: NodeId,
|
||||
prev_sibling: Option<NodeId>,
|
||||
) {
|
||||
if let Some(next_sibling_id) = self.get_node_next_sibling_id(current_id) {
|
||||
self.set_prev_sibling(next_sibling_id, prev_sibling);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_parent(&mut self, node_id: NodeId, parent_id: Option<NodeId>) {
|
||||
if let Some(node) = self.get_node_mut(node_id) {
|
||||
node.relatives.parent = parent_id;
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_prev_sibling(&mut self, node_id: NodeId, prev_sibling: Option<NodeId>) {
|
||||
if let Some(node) = self.get_node_mut(node_id) {
|
||||
node.relatives.prev_sibling = prev_sibling;
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_next_sibling(&mut self, node_id: NodeId, next_sibling: Option<NodeId>) {
|
||||
if let Some(node) = self.get_node_mut(node_id) {
|
||||
node.relatives.next_sibling = next_sibling;
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_first_child(&mut self, node_id: NodeId, first_child: Option<NodeId>) {
|
||||
if let Some(node) = self.get_node_mut(node_id) {
|
||||
node.relatives.first_child = first_child;
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_last_child(&mut self, node_id: NodeId, last_child: Option<NodeId>) {
|
||||
if let Some(node) = self.get_node_mut(node_id) {
|
||||
node.relatives.last_child = last_child;
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_node_prev_sibling_id(&self, node_id: NodeId) -> Option<NodeId> {
|
||||
if let Some(node) = self.get_node(node_id) {
|
||||
node.relatives.prev_sibling
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_node_next_sibling_id(&self, node_id: NodeId) -> Option<NodeId> {
|
||||
if let Some(node) = self.get_node(node_id) {
|
||||
node.relatives.next_sibling
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_node_relatives(&self, node_id: NodeId) -> Relatives {
|
||||
if let Some(node) = self.get_node(node_id) {
|
||||
node.relatives
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn drop_children(&mut self, node_id: NodeId) {
|
||||
let sub_tree_ids: Vec<NodeId> = self
|
||||
.get(node_id)
|
||||
.expect("node must exist")
|
||||
.traverse_level_order()
|
||||
.skip(1) // skip the "root" of the sub-tree, which is the "current" node
|
||||
.map(|node_ref| node_ref.node_id())
|
||||
.collect();
|
||||
|
||||
for id in sub_tree_ids {
|
||||
self.core_tree.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
fn orphan_children(&mut self, node_id: NodeId) {
|
||||
let child_ids: Vec<NodeId> = self
|
||||
.get(node_id)
|
||||
.expect("node must exist")
|
||||
.children()
|
||||
.map(|node_ref| node_ref.node_id())
|
||||
.collect();
|
||||
|
||||
for id in child_ids {
|
||||
self.set_parent(id, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn new_node_ref(&self, node_id: NodeId) -> NodeRef<T> {
|
||||
NodeRef::new(node_id, self)
|
||||
}
|
||||
|
||||
fn new_node_mut(&mut self, node_id: NodeId) -> NodeMut<T> {
|
||||
NodeMut::new(node_id, self)
|
||||
}
|
||||
|
||||
fn is_node_first_last_child(&self, node_id: NodeId) -> (bool, bool) {
|
||||
if let Some(node) = self.get_node(node_id) {
|
||||
node.relatives
|
||||
.parent
|
||||
.and_then(|parent_id| self.get_node(parent_id))
|
||||
.map(|parent| {
|
||||
let Relatives {
|
||||
first_child: first,
|
||||
last_child: last,
|
||||
..
|
||||
} = parent.relatives;
|
||||
(
|
||||
first.map(|child_id| child_id == node_id).unwrap_or(false),
|
||||
last.map(|child_id| child_id == node_id).unwrap_or(false),
|
||||
)
|
||||
})
|
||||
.unwrap_or((false, false))
|
||||
} else {
|
||||
(false, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Tree<T> {
|
||||
fn default() -> Self {
|
||||
TreeBuilder::new().build()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug> Tree<T> {
|
||||
/// Write formatted tree representation and nodes with debug formatting.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let mut tree = TreeBuilder::new().with_root(0).build();
|
||||
/// let mut root = tree.root_mut().unwrap();
|
||||
/// root.append(1)
|
||||
/// .append(2);
|
||||
/// root.append(3);
|
||||
/// let mut s = String::new();
|
||||
/// tree.write_formatted(&mut s).unwrap();
|
||||
/// assert_eq!(&s, "\
|
||||
/// 0
|
||||
/// ├── 1
|
||||
/// │ └── 2
|
||||
/// └── 3
|
||||
/// ");
|
||||
/// ```
|
||||
///
|
||||
/// Writes nothing if the tree is empty.
|
||||
///
|
||||
/// ```
|
||||
/// use slab_tree::tree::TreeBuilder;
|
||||
///
|
||||
/// let tree = TreeBuilder::<i32>::new().build();
|
||||
/// let mut s = String::new();
|
||||
/// tree.write_formatted(&mut s).unwrap();
|
||||
/// assert_eq!(&s, "");
|
||||
/// ```
|
||||
pub fn write_formatted<W: std::fmt::Write>(&self, w: &mut W) -> std::fmt::Result {
|
||||
if let Some(root) = self.root() {
|
||||
let node_id = root.node_id();
|
||||
let childn = 0;
|
||||
let level = 0;
|
||||
let last = vec![];
|
||||
let mut stack = vec![(node_id, childn, level, last)];
|
||||
while let Some((node_id, childn, level, last)) = stack.pop() {
|
||||
debug_assert_eq!(
|
||||
last.len(),
|
||||
level,
|
||||
"each previous level should indicate whether it has reached the last node"
|
||||
);
|
||||
let node = self
|
||||
.get(node_id)
|
||||
.expect("getting node of existing node ref id");
|
||||
if childn == 0 {
|
||||
for i in 1..level {
|
||||
if last[i - 1] {
|
||||
write!(w, " ")?;
|
||||
} else {
|
||||
write!(w, "│ ")?;
|
||||
}
|
||||
}
|
||||
if level > 0 {
|
||||
if last[level - 1] {
|
||||
write!(w, "└── ")?;
|
||||
} else {
|
||||
write!(w, "├── ")?;
|
||||
}
|
||||
}
|
||||
writeln!(w, "{:?}", node.data())?;
|
||||
}
|
||||
let mut children = node.children().skip(childn);
|
||||
if let Some(child) = children.next() {
|
||||
let mut next_last = last.clone();
|
||||
if children.next().is_some() {
|
||||
stack.push((node_id, childn + 1, level, last));
|
||||
next_last.push(false);
|
||||
} else {
|
||||
next_last.push(true);
|
||||
}
|
||||
stack.push((child.node_id(), 0, level + 1, next_last));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(tarpaulin, skip)]
|
||||
#[cfg(test)]
|
||||
mod tree_tests {
|
||||
use super::*;
|
||||
use crate::behaviors::RemoveBehavior::{DropChildren, OrphanChildren};
|
||||
|
||||
#[test]
|
||||
fn capacity() {
|
||||
let tree = TreeBuilder::new().with_root(1).with_capacity(5).build();
|
||||
assert_eq!(tree.capacity(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_id() {
|
||||
let tree = TreeBuilder::new().with_root(1).build();
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root = tree.get(root_id).unwrap();
|
||||
assert_eq!(root.data(), &1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_root_drop() {
|
||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
|
||||
tree.remove(root_id, RemoveBehavior::DropChildren);
|
||||
assert!(tree.root().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_root_orphan() {
|
||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
|
||||
tree.remove(root_id, RemoveBehavior::OrphanChildren);
|
||||
assert!(tree.root().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root() {
|
||||
let tree = TreeBuilder::new().with_root(1).build();
|
||||
let root = tree.root().expect("root doesn't exist?");
|
||||
assert_eq!(root.data(), &1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_mut() {
|
||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
let mut root = tree.root_mut().expect("root doesn't exist?");
|
||||
|
||||
assert_eq!(root.data(), &mut 1);
|
||||
|
||||
*root.data() = 2;
|
||||
assert_eq!(root.data(), &mut 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
let tree = TreeBuilder::new().with_root(1).build();
|
||||
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root = tree.get(root_id);
|
||||
assert!(root.is_some());
|
||||
|
||||
let root = root.unwrap();
|
||||
assert_eq!(root.data(), &1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_mut() {
|
||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root = tree.get_mut(root_id);
|
||||
assert!(root.is_some());
|
||||
|
||||
let mut root = root.unwrap();
|
||||
assert_eq!(root.data(), &mut 1);
|
||||
|
||||
*root.data() = 2;
|
||||
assert_eq!(root.data(), &mut 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_node() {
|
||||
let tree = TreeBuilder::new().with_root(1).build();
|
||||
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root = tree.get_node(root_id);
|
||||
assert!(root.is_some());
|
||||
|
||||
let root = root.unwrap();
|
||||
assert_eq!(root.data, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_node_mut() {
|
||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
|
||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
||||
let root = tree.get_node_mut(root_id);
|
||||
assert!(root.is_some());
|
||||
|
||||
let root = root.unwrap();
|
||||
assert_eq!(root.data, 1);
|
||||
|
||||
root.data = 2;
|
||||
assert_eq!(root.data, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_drop() {
|
||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
|
||||
let two_id;
|
||||
let three_id;
|
||||
let four_id;
|
||||
let five_id;
|
||||
{
|
||||
let mut root = tree.root_mut().expect("root doesn't exist?");
|
||||
two_id = root.append(2).node_id();
|
||||
three_id = root.append(3).node_id();
|
||||
four_id = root.append(4).node_id();
|
||||
}
|
||||
{
|
||||
five_id = tree
|
||||
.get_mut(three_id)
|
||||
.expect("three doesn't exist?")
|
||||
.append(5)
|
||||
.node_id();
|
||||
}
|
||||
|
||||
// 1
|
||||
// / | \
|
||||
// 2 3 4
|
||||
// |
|
||||
// 5
|
||||
|
||||
tree.remove(three_id, DropChildren);
|
||||
|
||||
let root = tree
|
||||
.get_node(tree.root_id().expect("tree doesn't exist?"))
|
||||
.unwrap();
|
||||
assert!(root.relatives.first_child.is_some());
|
||||
assert!(root.relatives.last_child.is_some());
|
||||
assert_eq!(root.relatives.first_child.unwrap(), two_id);
|
||||
assert_eq!(root.relatives.last_child.unwrap(), four_id);
|
||||
|
||||
let two = tree.get_node(two_id);
|
||||
assert!(two.is_some());
|
||||
|
||||
let two = two.unwrap();
|
||||
assert_eq!(two.relatives.next_sibling, Some(four_id));
|
||||
|
||||
let four = tree.get_node(four_id);
|
||||
assert!(four.is_some());
|
||||
|
||||
let four = four.unwrap();
|
||||
assert_eq!(four.relatives.prev_sibling, Some(two_id));
|
||||
|
||||
let five = tree.get_node(five_id);
|
||||
assert!(five.is_none());
|
||||
}
|
||||
|
||||
/// Test that there is no panic if caller tries to remove a removed node
|
||||
#[test]
|
||||
fn address_dropped() {
|
||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
let two_id = tree.root_mut().expect("root doesn't exist").node_id();
|
||||
tree.remove(two_id, DropChildren);
|
||||
tree.remove(two_id, DropChildren);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_orphan() {
|
||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
||||
|
||||
let two_id;
|
||||
let three_id;
|
||||
let four_id;
|
||||
let five_id;
|
||||
{
|
||||
let mut root = tree.root_mut().expect("root doesn't exist?");
|
||||
two_id = root.append(2).node_id();
|
||||
three_id = root.append(3).node_id();
|
||||
four_id = root.append(4).node_id();
|
||||
}
|
||||
{
|
||||
five_id = tree
|
||||
.get_mut(three_id)
|
||||
.expect("three doesn't exist?")
|
||||
.append(5)
|
||||
.node_id();
|
||||
}
|
||||
|
||||
// 1
|
||||
// / | \
|
||||
// 2 3 4
|
||||
// |
|
||||
// 5
|
||||
|
||||
tree.remove(three_id, OrphanChildren);
|
||||
|
||||
let root = tree
|
||||
.get_node(tree.root_id().expect("tree doesn't exist?"))
|
||||
.unwrap();
|
||||
assert!(root.relatives.first_child.is_some());
|
||||
assert!(root.relatives.last_child.is_some());
|
||||
assert_eq!(root.relatives.first_child.unwrap(), two_id);
|
||||
assert_eq!(root.relatives.last_child.unwrap(), four_id);
|
||||
|
||||
let two = tree.get_node(two_id);
|
||||
assert!(two.is_some());
|
||||
|
||||
let two = two.unwrap();
|
||||
assert_eq!(two.relatives.next_sibling, Some(four_id));
|
||||
|
||||
let four = tree.get_node(four_id);
|
||||
assert!(four.is_some());
|
||||
|
||||
let four = four.unwrap();
|
||||
assert_eq!(four.relatives.prev_sibling, Some(two_id));
|
||||
|
||||
let five = tree.get_node(five_id);
|
||||
assert!(five.is_some());
|
||||
|
||||
let five = five.unwrap();
|
||||
assert_eq!(five.relatives.parent, None);
|
||||
}
|
||||
}
|
97
src/app.rs
97
src/app.rs
@@ -1,77 +1,64 @@
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use crossterm::event::{Event, KeyEventKind};
|
||||
|
||||
use crate::input::keymap::KeyBinding;
|
||||
use crate::state::{Environment, State};
|
||||
use crate::state::action::ActionResult;
|
||||
use crate::state::event::EventResult;
|
||||
use crate::state::init::StateInitializer;
|
||||
use crate::state::view::View;
|
||||
|
||||
pub fn run(state_initializer: &StateInitializer, view: &mut View) -> io::Result<()> {
|
||||
let environment = Environment::try_from(&*view)?;
|
||||
let mut state = State::new(state_initializer, environment);
|
||||
pub fn run(start_path: &Path) -> std::io::Result<()> {
|
||||
View::restore_terminal_on_panic();
|
||||
let mut view = View::stdout()?;
|
||||
|
||||
loop {
|
||||
match state.handle_events() {
|
||||
EventResult::Nothing => {}
|
||||
|
||||
EventResult::Draw => {
|
||||
view.set_dirty(false);
|
||||
}
|
||||
|
||||
EventResult::Redraw => {
|
||||
view.set_dirty(true);
|
||||
}
|
||||
}
|
||||
|
||||
let environment = Environment::try_from(&view)?;
|
||||
let mut state = State::with_root_path(start_path, environment);
|
||||
|
||||
'render: loop {
|
||||
view.render(|frame| state.render(frame))?;
|
||||
|
||||
match handle_terminal_event(&mut state, crossterm::event::read()?) {
|
||||
ActionResult::Nothing => {
|
||||
continue;
|
||||
}
|
||||
|
||||
ActionResult::Draw => {
|
||||
view.set_dirty(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
ActionResult::Redraw => {
|
||||
view.set_dirty(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
ActionResult::PushLayer(layer) => {
|
||||
state.push_layer(layer);
|
||||
view.set_dirty(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
ActionResult::ReplaceLayer(layer) => {
|
||||
state.pop_layer();
|
||||
state.push_layer(layer);
|
||||
view.set_dirty(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
ActionResult::PopLayer => {
|
||||
if state.pop_layer() {
|
||||
break;
|
||||
} else {
|
||||
view.set_dirty(false);
|
||||
continue;
|
||||
'event: loop {
|
||||
match handle_event(&mut state, crossterm::event::read()?) {
|
||||
ActionResult::Nothing => {
|
||||
continue 'event;
|
||||
}
|
||||
|
||||
ActionResult::Draw => {
|
||||
continue 'render;
|
||||
}
|
||||
|
||||
ActionResult::Redraw => {
|
||||
view.clear()?;
|
||||
continue 'render;
|
||||
}
|
||||
|
||||
ActionResult::PushLayer(layer) => {
|
||||
state.push_layer(layer);
|
||||
continue 'render;
|
||||
}
|
||||
|
||||
ActionResult::ReplaceLayer(layer) => {
|
||||
state.pop_layer();
|
||||
state.push_layer(layer);
|
||||
continue 'render;
|
||||
}
|
||||
|
||||
ActionResult::PopLayer => {
|
||||
if state.pop_layer() {
|
||||
break 'render;
|
||||
} else {
|
||||
continue 'render;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
view.close()
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn handle_terminal_event(state: &mut State, event: Event) -> ActionResult {
|
||||
fn handle_event(state: &mut State, event: Event) -> ActionResult {
|
||||
if let Event::Key(key) = event {
|
||||
if key.kind == KeyEventKind::Release {
|
||||
ActionResult::Nothing
|
||||
|
@@ -69,7 +69,7 @@ impl<'a> InputFieldDialogBuilder5<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_confirm<F>(self, confirm_action: F) -> InputFieldDialogLayer<'a> where F: Fn(String) -> ActionResult + 'static {
|
||||
pub fn build(self, confirm_action: Box<dyn Fn(String) -> ActionResult>) -> InputFieldDialogLayer<'a> {
|
||||
let step4 = self.step4;
|
||||
let step3 = step4.step3;
|
||||
let step2 = step3.step2;
|
||||
|
@@ -12,7 +12,6 @@ use crate::component::input::InputField;
|
||||
use crate::input::keymap::KeyBinding;
|
||||
use crate::state::action::ActionResult;
|
||||
use crate::state::Environment;
|
||||
use crate::state::event::EventResult;
|
||||
use crate::state::layer::Layer;
|
||||
use crate::state::view::Frame;
|
||||
|
||||
@@ -30,9 +29,8 @@ pub struct InputFieldDialogLayer<'a> {
|
||||
}
|
||||
|
||||
impl<'a> InputFieldDialogLayer<'a> {
|
||||
fn new<F>(y: u16, min_width: u16, default_color: Color, darker_color: Color, title: Line<'a>, message: Text<'a>, initial_value: Option<String>, confirm_action: F) -> Self where F: Fn(String) -> ActionResult + 'static {
|
||||
fn new(y: u16, min_width: u16, default_color: Color, darker_color: Color, title: Line<'a>, message: Text<'a>, initial_value: Option<String>, confirm_action: Box<dyn Fn(String) -> ActionResult>) -> Self {
|
||||
let field = initial_value.map_or_else(InputField::new, InputField::with_text);
|
||||
let confirm_action = Box::new(confirm_action);
|
||||
Self { y, min_width, default_color, darker_color, title, message, field, confirm_action }
|
||||
}
|
||||
|
||||
@@ -60,10 +58,6 @@ impl<'a> Layer for InputFieldDialogLayer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_events(&mut self, _environment: &Environment) -> EventResult {
|
||||
EventResult::Nothing
|
||||
}
|
||||
|
||||
fn render(&mut self, frame: &mut Frame) {
|
||||
let message_width = u16::try_from(self.message.width()).unwrap_or(u16::MAX);
|
||||
let message_height = u16::try_from(self.message.height()).unwrap_or(u16::MAX);
|
||||
|
@@ -37,9 +37,9 @@ impl<'a> MessageDialogActionMap<'a> {
|
||||
])
|
||||
}
|
||||
|
||||
pub fn yes_no<F>(yes_action: F) -> Self where F: Fn() -> ActionResult + 'static {
|
||||
pub fn yes_no(yes_action: Box<dyn Fn() -> ActionResult>) -> Self {
|
||||
let mut map = ActionHashMap::new();
|
||||
map.insert(KeyBinding::char('y'), Box::new(yes_action));
|
||||
map.insert(KeyBinding::char('y'), yes_action);
|
||||
map.insert(KeyBinding::char('n'), Box::new(|| ActionResult::PopLayer));
|
||||
|
||||
Self::new(map, vec![
|
||||
|
@@ -63,7 +63,7 @@ impl<'a> MessageDialogBuilder4<'a> {
|
||||
self.actions(MessageDialogActionMap::ok())
|
||||
}
|
||||
|
||||
pub fn yes_no<F>(self, yes_action: F) -> MessageDialogLayer<'a> where F: Fn() -> ActionResult + 'static {
|
||||
pub fn yes_no(self, yes_action: Box<dyn Fn() -> ActionResult>) -> MessageDialogLayer<'a> {
|
||||
self.actions(MessageDialogActionMap::yes_no(yes_action))
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,6 @@ use crate::component::dialog::render_dialog_border;
|
||||
use crate::input::keymap::KeyBinding;
|
||||
use crate::state::action::ActionResult;
|
||||
use crate::state::Environment;
|
||||
use crate::state::event::EventResult;
|
||||
use crate::state::layer::Layer;
|
||||
use crate::state::view::Frame;
|
||||
|
||||
@@ -41,7 +40,7 @@ impl<'a> MessageDialogLayer<'a> {
|
||||
MessageDialogBuilder
|
||||
}
|
||||
|
||||
pub fn error(y: u16, message: impl Into<Text<'a>>) -> MessageDialogLayer<'a> {
|
||||
pub fn generic_error(y: u16, message: impl Into<Text<'a>>) -> MessageDialogLayer<'a> {
|
||||
Self::build()
|
||||
.y(y)
|
||||
.color(Color::LightRed)
|
||||
@@ -56,10 +55,6 @@ impl Layer for MessageDialogLayer<'_> {
|
||||
self.actions.handle_input(key_binding)
|
||||
}
|
||||
|
||||
fn handle_events(&mut self, _environment: &Environment) -> EventResult {
|
||||
EventResult::Nothing
|
||||
}
|
||||
|
||||
fn render(&mut self, frame: &mut Frame) {
|
||||
let content_width = u16::try_from(self.message.width()).unwrap_or(u16::MAX);
|
||||
let content_height = u16::try_from(self.message.height()).unwrap_or(u16::MAX);
|
||||
|
@@ -22,10 +22,9 @@ impl Action<FsLayer> for RedrawScreen {
|
||||
pub struct EnterCommandMode;
|
||||
|
||||
impl Action<FsLayer> for EnterCommandMode {
|
||||
fn perform(&self, layer: &mut FsLayer, _environment: &Environment) -> ActionResult {
|
||||
ActionResult::push_layer(InputFieldOverlayLayer::new(":", move |command| {
|
||||
// command.split_once(" ")
|
||||
fn perform(&self, _layer: &mut FsLayer, _environment: &Environment) -> ActionResult {
|
||||
ActionResult::PushLayer(Box::new(InputFieldOverlayLayer::new(":", Box::new(|command| {
|
||||
ActionResult::PopLayer
|
||||
}))
|
||||
}))))
|
||||
}
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ impl Action<FsLayer> for PushCountDigit {
|
||||
if new_count > MAX_COUNT {
|
||||
layer.registers.count = None;
|
||||
|
||||
ActionResult::push_layer(MessageDialogLayer::error(layer.dialog_y(), format!("Count is too large (> {MAX_COUNT}), it will be reset.")))
|
||||
ActionResult::PushLayer(Box::new(MessageDialogLayer::generic_error(layer.dialog_y(), format!("Count is too large (> {MAX_COUNT}), it will be reset."))))
|
||||
} else {
|
||||
layer.registers.count = Some(new_count);
|
||||
ActionResult::Nothing
|
||||
|
@@ -1,17 +1,19 @@
|
||||
use std::{fs, io};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
||||
use ratatui::style::Color;
|
||||
|
||||
use slab_tree::NodeId;
|
||||
|
||||
use crate::component::dialog::input::InputFieldDialogLayer;
|
||||
use crate::component::dialog::message::MessageDialogLayer;
|
||||
use crate::component::filesystem::action::file::{FileNode, get_selected_file, RefreshParentDirectoryAndSelectFile};
|
||||
use crate::component::filesystem::action::file::{FileNode, get_selected_file};
|
||||
use crate::component::filesystem::event::FsLayerEvent;
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::file::FileKind;
|
||||
use crate::state::action::{Action, ActionResult};
|
||||
use crate::state::Environment;
|
||||
use crate::util::slab_tree::NodeRefExtensions;
|
||||
|
||||
trait CreateEntry {
|
||||
fn title() -> &'static str;
|
||||
@@ -69,7 +71,7 @@ impl Action<FsLayer> for CreateDirectoryInSelectedDirectory {
|
||||
|
||||
fn create_in_selected_directory<T: CreateEntry>(layer: &mut FsLayer) -> ActionResult {
|
||||
if let Some(FileNode { node, path, .. }) = get_selected_file(layer).filter(|n| matches!(n.entry.kind(), FileKind::Directory)) {
|
||||
ActionResult::push_layer(create_new_name_prompt::<T>(layer, path.to_owned(), node.node_id()))
|
||||
ActionResult::PushLayer(Box::new(create_new_name_prompt::<T>(layer, path.to_owned(), node.node_id())))
|
||||
} else {
|
||||
ActionResult::Nothing
|
||||
}
|
||||
@@ -93,7 +95,7 @@ impl Action<FsLayer> for CreateDirectoryInParentOfSelectedEntry {
|
||||
|
||||
fn create_in_parent_of_selected_file<T: CreateEntry>(layer: &mut FsLayer) -> ActionResult {
|
||||
if let Some((parent_node_id, parent_path)) = get_parent_of_selected_file(layer) {
|
||||
ActionResult::push_layer(create_new_name_prompt::<T>(layer, parent_path.to_owned(), parent_node_id))
|
||||
ActionResult::PushLayer(Box::new(create_new_name_prompt::<T>(layer, parent_path.to_owned(), parent_node_id)))
|
||||
} else {
|
||||
ActionResult::Nothing
|
||||
}
|
||||
@@ -103,9 +105,9 @@ fn get_parent_of_selected_file(layer: &FsLayer) -> Option<(NodeId, &Path)> {
|
||||
get_selected_file(layer).and_then(|n| { Some((n.node.parent_id()?, n.path.parent()?)) })
|
||||
}
|
||||
|
||||
fn create_new_name_prompt<'b, T: CreateEntry>(layer: &FsLayer, parent_folder: PathBuf, parent_view_node_id: NodeId) -> InputFieldDialogLayer<'b> {
|
||||
fn create_new_name_prompt<'b, T: CreateEntry>(layer: &FsLayer, parent_folder: PathBuf, view_node_id_to_refresh: NodeId) -> InputFieldDialogLayer<'b> {
|
||||
let y = layer.dialog_y();
|
||||
let events = layer.events();
|
||||
let pending_events = Rc::clone(&layer.pending_events);
|
||||
|
||||
InputFieldDialogLayer::build()
|
||||
.y(y)
|
||||
@@ -113,24 +115,25 @@ fn create_new_name_prompt<'b, T: CreateEntry>(layer: &FsLayer, parent_folder: Pa
|
||||
.color(Color::LightCyan, Color::Cyan)
|
||||
.title(T::title())
|
||||
.message(format!("Creating {} in {}", T::kind(), parent_folder.to_string_lossy()))
|
||||
.on_confirm(move |new_name| {
|
||||
.build(Box::new(move |new_name| {
|
||||
if new_name.is_empty() {
|
||||
return ActionResult::Nothing;
|
||||
}
|
||||
|
||||
let new_path = parent_folder.join(&new_name);
|
||||
if new_path.exists() {
|
||||
return ActionResult::push_layer(MessageDialogLayer::error(y, "Something with this name already exists."));
|
||||
return ActionResult::PushLayer(Box::new(MessageDialogLayer::generic_error(y, "Something with this name already exists.")));
|
||||
}
|
||||
|
||||
match T::create(new_path) {
|
||||
Ok(_) => {
|
||||
events.enqueue(RefreshParentDirectoryAndSelectFile { parent_view_node_id, child_file_name: new_name });
|
||||
FsLayerEvent::RefreshViewNodeChildren(view_node_id_to_refresh).enqueue(&pending_events);
|
||||
FsLayerEvent::SelectViewNodeChildByFileName(view_node_id_to_refresh, new_name).enqueue(&pending_events);
|
||||
ActionResult::PopLayer
|
||||
}
|
||||
Err(e) => {
|
||||
ActionResult::push_layer(MessageDialogLayer::error(y, format!("Could not create {}: {e}", T::kind())))
|
||||
ActionResult::PushLayer(Box::new(MessageDialogLayer::generic_error(y, format!("Could not create {}: {e}", T::kind()))))
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use ratatui::style::Color;
|
||||
@@ -8,11 +9,11 @@ use slab_tree::NodeId;
|
||||
|
||||
use crate::component::dialog::message::MessageDialogLayer;
|
||||
use crate::component::filesystem::action::file::{FileNode, get_entry_kind_name, get_selected_file};
|
||||
use crate::component::filesystem::event::FsLayerEvent;
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::file::{FileEntry, FileKind};
|
||||
use crate::state::action::{Action, ActionResult};
|
||||
use crate::state::Environment;
|
||||
use crate::state::event::EventResult;
|
||||
|
||||
pub struct DeleteSelectedEntry;
|
||||
|
||||
@@ -28,7 +29,7 @@ impl Action<FsLayer> for DeleteSelectedEntry {
|
||||
|
||||
fn create_delete_confirmation_dialog<'a>(layer: &FsLayer, view_node_id: NodeId, entry: &FileEntry, path: PathBuf) -> MessageDialogLayer<'a> {
|
||||
let y = layer.dialog_y();
|
||||
let events = layer.events();
|
||||
let pending_events = Rc::clone(&layer.pending_events);
|
||||
|
||||
let total_files = if matches!(entry.kind(), FileKind::Directory) {
|
||||
count_files(path.clone())
|
||||
@@ -44,17 +45,17 @@ fn create_delete_confirmation_dialog<'a>(layer: &FsLayer, view_node_id: NodeId,
|
||||
Line::from(format!("Permanently delete {}?", path.to_string_lossy())),
|
||||
Line::from(format!("This will affect {}.", total_files.describe())),
|
||||
])
|
||||
.yes_no(move || {
|
||||
.yes_no(Box::new(move || {
|
||||
match delete_path_recursively(&path) {
|
||||
Ok(_) => {
|
||||
events.enqueue_fn(move |layer, _| EventResult::draw_if(layer.tree.delete_node(view_node_id)));
|
||||
FsLayerEvent::DeleteViewNode(view_node_id).enqueue(&pending_events);
|
||||
ActionResult::PopLayer
|
||||
}
|
||||
Err(e) => {
|
||||
ActionResult::replace_layer(MessageDialogLayer::error(y.saturating_add(1), e.to_string()))
|
||||
ActionResult::ReplaceLayer(Box::new(MessageDialogLayer::generic_error(y.saturating_add(1), e.to_string())))
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
const MAX_COUNT_TIME: Duration = Duration::from_secs(5);
|
||||
|
@@ -8,11 +8,12 @@ use slab_tree::NodeRef;
|
||||
|
||||
use crate::component::dialog::message::MessageDialogLayer;
|
||||
use crate::component::filesystem::action::file::{FileNode, get_selected_file};
|
||||
use crate::component::filesystem::event::FsLayerEvent;
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::component::filesystem::tree::FsTreeViewNode;
|
||||
use crate::state::action::{Action, ActionResult};
|
||||
use crate::state::Environment;
|
||||
use crate::state::event::EventResult;
|
||||
use crate::util::slab_tree::NodeRefExtensions;
|
||||
|
||||
pub struct EditSelectedEntry;
|
||||
|
||||
@@ -33,12 +34,12 @@ fn open_default_editor(layer: &FsLayer, node: &NodeRef<FsTreeViewNode>, path: &P
|
||||
.status();
|
||||
|
||||
if status.is_err_and(|e| e.kind() == ErrorKind::NotFound) {
|
||||
return ActionResult::push_layer(MessageDialogLayer::error(layer.dialog_y(), format!("Default editor '{}' not found.", editor.to_string_lossy())));
|
||||
return ActionResult::PushLayer(Box::new(MessageDialogLayer::generic_error(layer.dialog_y(), format!("Default editor '{}' not found.", editor.to_string_lossy()))));
|
||||
}
|
||||
|
||||
// Refresh the parent directory, or the root node if this is the view root.
|
||||
let node_id_to_refresh = node.parent_id().unwrap_or_else(|| node.node_id());
|
||||
layer.events().enqueue_fn(move |layer, _| EventResult::draw_if(layer.tree.refresh_children(node_id_to_refresh)));
|
||||
FsLayerEvent::RefreshViewNodeChildren(node_id_to_refresh).enqueue(&layer.pending_events);
|
||||
|
||||
ActionResult::Redraw
|
||||
}
|
||||
|
@@ -1,13 +1,11 @@
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use slab_tree::{NodeId, NodeRef};
|
||||
use slab_tree::NodeRef;
|
||||
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::component::filesystem::tree::FsTreeViewNode;
|
||||
use crate::file::{FileEntry, FileKind};
|
||||
use crate::state::Environment;
|
||||
use crate::state::event::{Event, EventResult};
|
||||
|
||||
pub use self::create::*;
|
||||
pub use self::delete::*;
|
||||
@@ -20,7 +18,7 @@ mod edit;
|
||||
mod rename;
|
||||
|
||||
fn get_selected_file(layer: &FsLayer) -> Option<FileNode> {
|
||||
if let Some(node) = layer.tree.selected_node() {
|
||||
if let Some(node) = layer.selected_node() {
|
||||
if let Some(entry) = layer.tree.get_entry(&node) {
|
||||
if let Some(path) = entry.path() {
|
||||
return Some(FileNode { node, entry, path });
|
||||
@@ -56,19 +54,3 @@ fn format_io_error(err: &io::Error) -> String {
|
||||
str.push('.');
|
||||
str
|
||||
}
|
||||
|
||||
struct RefreshParentDirectoryAndSelectFile {
|
||||
parent_view_node_id: NodeId,
|
||||
child_file_name: String,
|
||||
}
|
||||
|
||||
impl Event<FsLayer> for RefreshParentDirectoryAndSelectFile {
|
||||
fn dispatch(&self, layer: &mut FsLayer, _environment: &Environment) -> EventResult {
|
||||
if layer.tree.refresh_children(self.parent_view_node_id) {
|
||||
layer.tree.select_child_node_by_name(self.parent_view_node_id, &self.child_file_name);
|
||||
EventResult::Draw
|
||||
} else {
|
||||
EventResult::Nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,19 +1,21 @@
|
||||
use io::ErrorKind;
|
||||
use std::{fs, io};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ratatui::style::Color;
|
||||
|
||||
use slab_tree::NodeRef;
|
||||
|
||||
use crate::component::dialog::input::InputFieldDialogLayer;
|
||||
use crate::component::dialog::message::MessageDialogLayer;
|
||||
use crate::component::filesystem::action::file::{FileNode, format_io_error, get_entry_kind_name, get_selected_file, RefreshParentDirectoryAndSelectFile};
|
||||
use crate::component::filesystem::action::file::{FileNode, format_io_error, get_entry_kind_name, get_selected_file};
|
||||
use crate::component::filesystem::event::FsLayerEvent;
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::component::filesystem::tree::FsTreeViewNode;
|
||||
use crate::file::FileEntry;
|
||||
use crate::state::action::{Action, ActionResult};
|
||||
use crate::state::Environment;
|
||||
use crate::util::slab_tree::NodeRefExtensions;
|
||||
|
||||
pub struct RenameSelectedEntry {
|
||||
pub prefill: bool,
|
||||
@@ -22,7 +24,7 @@ pub struct RenameSelectedEntry {
|
||||
impl Action<FsLayer> for RenameSelectedEntry {
|
||||
fn perform(&self, layer: &mut FsLayer, _environment: &Environment) -> ActionResult {
|
||||
if let Some(FileNode { node, entry, path }) = get_selected_file(layer) {
|
||||
ActionResult::push_layer(self.create_rename_dialog(layer, &node, entry, path.to_owned()))
|
||||
ActionResult::PushLayer(Box::new(self.create_rename_dialog(layer, &node, entry, path.to_owned())))
|
||||
} else {
|
||||
ActionResult::Nothing
|
||||
}
|
||||
@@ -33,8 +35,8 @@ impl RenameSelectedEntry {
|
||||
#[allow(clippy::wildcard_enum_match_arm)]
|
||||
fn create_rename_dialog<'a, 'b>(&'a self, layer: &'a FsLayer, node: &'a NodeRef<FsTreeViewNode>, entry: &'a FileEntry, path: PathBuf) -> InputFieldDialogLayer<'b> {
|
||||
let y = layer.dialog_y();
|
||||
let events = layer.events();
|
||||
let parent_view_node_id = node.parent_id();
|
||||
let parent_node_id = node.parent_id();
|
||||
let pending_events = Rc::clone(&layer.pending_events);
|
||||
|
||||
InputFieldDialogLayer::build()
|
||||
.y(y)
|
||||
@@ -43,19 +45,20 @@ impl RenameSelectedEntry {
|
||||
.title(format!("Rename {}", get_entry_kind_name(entry)))
|
||||
.message(format!("Renaming {}", path.to_string_lossy()))
|
||||
.initial_value(self.prefill.then(|| entry.name().str().to_owned()))
|
||||
.on_confirm(move |new_name| {
|
||||
.build(Box::new(move |new_name| {
|
||||
match rename_file(&path, &new_name) {
|
||||
Ok(_) => {
|
||||
if let Some(parent_view_node_id) = parent_view_node_id {
|
||||
events.enqueue(RefreshParentDirectoryAndSelectFile { parent_view_node_id, child_file_name: new_name });
|
||||
if let Some(parent_node_id) = parent_node_id {
|
||||
FsLayerEvent::RefreshViewNodeChildren(parent_node_id).enqueue(&pending_events);
|
||||
FsLayerEvent::SelectViewNodeChildByFileName(parent_node_id, new_name).enqueue(&pending_events);
|
||||
}
|
||||
ActionResult::PopLayer
|
||||
}
|
||||
Err(e) => {
|
||||
ActionResult::push_layer(MessageDialogLayer::error(y.saturating_add(1), format_io_error(&e)))
|
||||
ActionResult::PushLayer(Box::new(MessageDialogLayer::generic_error(y.saturating_add(1), format_io_error(&e))))
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,105 @@
|
||||
pub mod application;
|
||||
pub mod count;
|
||||
pub mod file;
|
||||
pub mod movement;
|
||||
pub mod tree;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::component::filesystem::action::application::{EnterCommandMode, Quit, RedrawScreen};
|
||||
use crate::component::filesystem::action::count::PushCountDigit;
|
||||
use crate::component::filesystem::action::file::{CreateDirectoryInParentOfSelectedEntry, CreateDirectoryInSelectedDirectory, CreateFileInParentOfSelectedEntry, CreateFileInSelectedDirectory, DeleteSelectedEntry, EditSelectedEntry, RenameSelectedEntry};
|
||||
use crate::component::filesystem::action::movement::{CollapseSelectedOr, ExpandSelectedOr, MoveBetweenFirstAndLastSibling, MoveDown, MovementWithCountFactory, MovementWithFallbackFactory, MoveOrTraverseUpParent, MoveToFirst, MoveToLast, MoveToLineOr, MoveToNextSibling, MoveToParent, MoveToPreviousSibling, MoveUp, ScreenHeightRatio};
|
||||
use crate::component::filesystem::action::tree::{ExpandCollapse, RefreshChildrenOfSelected};
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::input::keymap::KeyMap;
|
||||
use crate::state::action::Action;
|
||||
|
||||
mod application;
|
||||
mod count;
|
||||
mod file;
|
||||
mod movement;
|
||||
mod tree;
|
||||
|
||||
type ActionKeyMap = KeyMap<Box<dyn Action<FsLayer> + Sync>>;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref ACTION_MAP: ActionKeyMap = create_action_map();
|
||||
}
|
||||
|
||||
fn create_action_map() -> ActionKeyMap {
|
||||
let mut me = ActionKeyMap::new();
|
||||
|
||||
map(&mut me, "0", PushCountDigit(0));
|
||||
map(&mut me, "1", PushCountDigit(1));
|
||||
map(&mut me, "2", PushCountDigit(2));
|
||||
map(&mut me, "3", PushCountDigit(3));
|
||||
map(&mut me, "4", PushCountDigit(4));
|
||||
map(&mut me, "5", PushCountDigit(5));
|
||||
map(&mut me, "6", PushCountDigit(6));
|
||||
map(&mut me, "7", PushCountDigit(7));
|
||||
map(&mut me, "8", PushCountDigit(8));
|
||||
map(&mut me, "9", PushCountDigit(9));
|
||||
|
||||
map(&mut me, "af", CreateFileInSelectedDirectory);
|
||||
map(&mut me, "ad", CreateDirectoryInSelectedDirectory);
|
||||
map(&mut me, "e", EditSelectedEntry);
|
||||
map(&mut me, "d", DeleteSelectedEntry);
|
||||
map(&mut me, "gg", MoveToLineOr(MoveToFirst));
|
||||
map(&mut me, "G", MoveToLineOr(MoveToLast));
|
||||
map(&mut me, "h", CollapseSelectedOr(MoveToParent));
|
||||
map(&mut me, "H", MoveOrTraverseUpParent);
|
||||
map(&mut me, "if", CreateFileInSelectedDirectory);
|
||||
map(&mut me, "id", CreateDirectoryInSelectedDirectory);
|
||||
map(&mut me, "j", MoveDown);
|
||||
map(&mut me, "J", MoveToNextSibling.with_fallback(MoveDown));
|
||||
map(&mut me, "k", MoveUp);
|
||||
map(&mut me, "K", MoveToPreviousSibling.with_fallback(MoveUp));
|
||||
map(&mut me, "l", ExpandSelectedOr(MoveDown));
|
||||
map(&mut me, "of", CreateFileInParentOfSelectedEntry);
|
||||
map(&mut me, "od", CreateDirectoryInParentOfSelectedEntry);
|
||||
map(&mut me, "q", Quit);
|
||||
map(&mut me, "r", RenameSelectedEntry { prefill: true });
|
||||
map(&mut me, "R", RenameSelectedEntry { prefill: false });
|
||||
|
||||
map(&mut me, "%", MoveBetweenFirstAndLastSibling);
|
||||
map(&mut me, ":", EnterCommandMode);
|
||||
|
||||
map(&mut me, "<Ctrl-B>", MoveUp.with_custom_count(ScreenHeightRatio(1)));
|
||||
map(&mut me, "<Ctrl-C>", Quit);
|
||||
map(&mut me, "<Ctrl-D>", MoveDown.with_default_count(ScreenHeightRatio(2)));
|
||||
map(&mut me, "<Ctrl-F>", MoveDown.with_custom_count(ScreenHeightRatio(1)));
|
||||
map(&mut me, "<Ctrl-L>", RedrawScreen);
|
||||
map(&mut me, "<Ctrl-N>", MoveDown);
|
||||
map(&mut me, "<Ctrl-P>", MoveUp);
|
||||
map(&mut me, "<Ctrl-U>", MoveUp.with_default_count(ScreenHeightRatio(2)));
|
||||
|
||||
map(&mut me, "<Space>", ExpandCollapse { default_depth: 1 });
|
||||
map(&mut me, "<Ctrl-Space>", ExpandCollapse { default_depth: 1000 });
|
||||
|
||||
map(&mut me, "<Down>", MoveDown);
|
||||
map(&mut me, "<Shift-Down>", MoveDown.with_custom_count(ScreenHeightRatio(1)));
|
||||
map(&mut me, "<Alt-Down>", MoveToNextSibling.with_fallback(MoveDown));
|
||||
|
||||
map(&mut me, "<Up>", MoveUp);
|
||||
map(&mut me, "<Shift-Up>", MoveUp.with_custom_count(ScreenHeightRatio(1)));
|
||||
map(&mut me, "<Alt-Up>", MoveToPreviousSibling.with_fallback(MoveUp));
|
||||
|
||||
map(&mut me, "<Left>", CollapseSelectedOr(MoveToParent));
|
||||
map(&mut me, "<Alt-Left>", MoveOrTraverseUpParent);
|
||||
|
||||
map(&mut me, "<Right>", ExpandSelectedOr(MoveDown));
|
||||
|
||||
map(&mut me, "<Del>", DeleteSelectedEntry);
|
||||
|
||||
map(&mut me, "<PageDown>", MoveDown.with_custom_count(ScreenHeightRatio(1)));
|
||||
map(&mut me, "<PageUp>", MoveUp.with_custom_count(ScreenHeightRatio(1)));
|
||||
|
||||
map(&mut me, "<F2>", RenameSelectedEntry { prefill: true });
|
||||
map(&mut me, "<Shift-F2>", RenameSelectedEntry { prefill: false });
|
||||
|
||||
map(&mut me, "<F5>", RefreshChildrenOfSelected);
|
||||
|
||||
me
|
||||
}
|
||||
|
||||
fn map(map: &mut ActionKeyMap, key_binding_str: &str, action: impl Action<FsLayer> + Sync + 'static) {
|
||||
// Panic on error, since we are hard-coding the key bindings.
|
||||
if let Err(err) = map.insert(key_binding_str, Box::new(action)) {
|
||||
panic!("Failed to insert key binding '{}'. {}", err.sequence(), err.error());
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +1,14 @@
|
||||
use slab_tree::NodeId;
|
||||
|
||||
use crate::component::filesystem::action::movement::{get_simple_movement_target, MovementAction, perform_movement_with_count_from_register, SimpleMovementAction};
|
||||
use crate::component::filesystem::action::movement::{get_simple_movement_target, MovementAction, perform_movement_with_count, SimpleMovementAction};
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::component::filesystem::tree::FsTree;
|
||||
use crate::state::Environment;
|
||||
|
||||
pub struct ExpandSelectedOr<M: SimpleMovementAction>(pub M);
|
||||
|
||||
impl<M: SimpleMovementAction> MovementAction for ExpandSelectedOr<M> {
|
||||
fn get_target(&self, layer: &mut FsLayer, _environment: &Environment) -> Option<NodeId> where Self: Sized {
|
||||
Some(perform_action_or_movement::<M, _>(layer, FsTree::expand))
|
||||
Some(perform_action_or_movement::<M, _>(layer, FsLayer::expand))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,16 +16,16 @@ pub struct CollapseSelectedOr<M: SimpleMovementAction>(pub M);
|
||||
|
||||
impl<M: SimpleMovementAction> MovementAction for CollapseSelectedOr<M> {
|
||||
fn get_target(&self, layer: &mut FsLayer, _environment: &Environment) -> Option<NodeId> where Self: Sized {
|
||||
Some(perform_action_or_movement::<M, _>(layer, FsTree::collapse))
|
||||
Some(perform_action_or_movement::<M, _>(layer, FsLayer::collapse))
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_action_or_movement<M: SimpleMovementAction, F>(layer: &mut FsLayer, action: F) -> NodeId where F: Fn(&mut FsTree, NodeId) -> bool {
|
||||
perform_movement_with_count_from_register(layer, |tree, node_id| {
|
||||
if action(tree, node_id) {
|
||||
fn perform_action_or_movement<M: SimpleMovementAction, F>(layer: &mut FsLayer, action: F) -> NodeId where F: Fn(&mut FsLayer, NodeId) -> bool {
|
||||
perform_movement_with_count(layer, layer.registers.count, |layer, node_id| {
|
||||
if action(layer, node_id) {
|
||||
Some(node_id)
|
||||
} else {
|
||||
get_simple_movement_target::<M>(tree, node_id)
|
||||
get_simple_movement_target::<M>(layer, node_id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -1,14 +1,15 @@
|
||||
use slab_tree::{NodeId, NodeRef};
|
||||
|
||||
use crate::component::filesystem::action::movement::{MovementAction, perform_movement_with_count_from_register, SimpleMovementAction};
|
||||
use crate::component::filesystem::action::movement::{MovementAction, perform_movement_with_count, SimpleMovementAction};
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::component::filesystem::tree::{FsTree, FsTreeViewNode};
|
||||
use crate::component::filesystem::tree::{FsTreeView, FsTreeViewNode};
|
||||
use crate::state::Environment;
|
||||
use crate::util::slab_tree::NodeRefExtensions;
|
||||
|
||||
pub struct MoveToNextSibling;
|
||||
|
||||
impl SimpleMovementAction for MoveToNextSibling {
|
||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
fn get_target(_view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
selected_node.next_sibling_id()
|
||||
}
|
||||
}
|
||||
@@ -16,7 +17,7 @@ impl SimpleMovementAction for MoveToNextSibling {
|
||||
pub struct MoveToPreviousSibling;
|
||||
|
||||
impl SimpleMovementAction for MoveToPreviousSibling {
|
||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
fn get_target(_view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
selected_node.prev_sibling_id()
|
||||
}
|
||||
}
|
||||
@@ -24,7 +25,7 @@ impl SimpleMovementAction for MoveToPreviousSibling {
|
||||
pub struct MoveBetweenFirstAndLastSibling;
|
||||
|
||||
impl SimpleMovementAction for MoveBetweenFirstAndLastSibling {
|
||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
fn get_target(_view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
if selected_node.next_sibling().is_none() {
|
||||
selected_node.parent().and_then(|node| node.first_child_id())
|
||||
} else {
|
||||
@@ -36,7 +37,7 @@ impl SimpleMovementAction for MoveBetweenFirstAndLastSibling {
|
||||
pub struct MoveToParent;
|
||||
|
||||
impl SimpleMovementAction for MoveToParent {
|
||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
fn get_target(_view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
selected_node.parent_id()
|
||||
}
|
||||
}
|
||||
@@ -45,17 +46,19 @@ pub struct MoveOrTraverseUpParent;
|
||||
|
||||
impl MovementAction for MoveOrTraverseUpParent {
|
||||
fn get_target(&self, layer: &mut FsLayer, _environment: &Environment) -> Option<NodeId> where Self: Sized {
|
||||
Some(perform_movement_with_count_from_register(layer, Self::get_target))
|
||||
Some(perform_movement_with_count(layer, layer.registers.count, Self::get_target))
|
||||
}
|
||||
}
|
||||
|
||||
impl MoveOrTraverseUpParent {
|
||||
fn get_target(tree: &mut FsTree, node_id: NodeId) -> Option<NodeId> {
|
||||
if let Some(node) = tree.get_view_node(node_id) {
|
||||
let target_node_id = <MoveToParent as SimpleMovementAction>::get_target(&node);
|
||||
fn get_target(layer: &mut FsLayer, node_id: NodeId) -> Option<NodeId> {
|
||||
let view = &layer.tree.view;
|
||||
|
||||
if let Some(node) = view.get(node_id) {
|
||||
let target_node_id = <MoveToParent as SimpleMovementAction>::get_target(view, &node);
|
||||
if target_node_id.is_some() {
|
||||
return target_node_id;
|
||||
} else if let Some(target_node_id) = tree.traverse_up_root() {
|
||||
} else if let Some(target_node_id) = layer.traverse_up_root() {
|
||||
return Some(target_node_id)
|
||||
}
|
||||
}
|
||||
|
@@ -2,15 +2,15 @@ use slab_tree::{NodeId, NodeRef};
|
||||
|
||||
use crate::component::filesystem::action::movement::{get_simple_movement_target, MovementAction, MoveToParent, perform_movement_with_count_from, SimpleMovementAction};
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::component::filesystem::tree::{FsTree, FsTreeViewNode};
|
||||
use crate::component::filesystem::tree::{FsTreeView, FsTreeViewNode};
|
||||
use crate::state::Environment;
|
||||
|
||||
/// Moves up `count` lines (1 line by default).
|
||||
pub struct MoveUp;
|
||||
|
||||
impl SimpleMovementAction for MoveUp {
|
||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
selected_node.above_id()
|
||||
fn get_target(tree: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
tree.get_node_above(selected_node)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ impl SimpleMovementAction for MoveUp {
|
||||
pub struct MoveDown;
|
||||
|
||||
impl SimpleMovementAction for MoveDown {
|
||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||
selected_node.below_id()
|
||||
fn get_target(tree: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||
tree.get_node_below(selected_node)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,15 +28,16 @@ pub struct MoveToFirst;
|
||||
|
||||
impl MovementAction for MoveToFirst {
|
||||
fn get_target(&self, layer: &mut FsLayer, _environment: &Environment) -> Option<NodeId> where Self: Sized {
|
||||
Some(Self::get_target(&mut layer.tree))
|
||||
Some(Self::get_target(layer))
|
||||
}
|
||||
}
|
||||
|
||||
impl MoveToFirst {
|
||||
fn get_target(tree: &mut FsTree) -> NodeId where Self: Sized {
|
||||
let mut target_node_id = tree.selected_view_node_id;
|
||||
fn get_target(layer: &mut FsLayer) -> NodeId where Self: Sized {
|
||||
let view = &layer.tree.view;
|
||||
let mut target_node_id = layer.selected_view_node_id;
|
||||
|
||||
while let Some(node_id) = tree.get_view_node(target_node_id).and_then(|node| <MoveToParent as SimpleMovementAction>::get_target(&node)) {
|
||||
while let Some(node_id) = view.get(target_node_id).and_then(|node| <MoveToParent as SimpleMovementAction>::get_target(view, &node)) {
|
||||
target_node_id = node_id;
|
||||
}
|
||||
|
||||
@@ -49,9 +50,9 @@ pub struct MoveToLast;
|
||||
|
||||
impl MovementAction for MoveToLast {
|
||||
fn get_target(&self, layer: &mut FsLayer, _environment: &Environment) -> Option<NodeId> where Self: Sized {
|
||||
let first_node_id = MoveToFirst::get_target(&mut layer.tree);
|
||||
let last_node_id = layer.tree.get_view_node(first_node_id).map(|node| node.last_descendant_or_self().node_id());
|
||||
last_node_id
|
||||
let first_node_id = MoveToFirst::get_target(layer);
|
||||
let last_node_id = layer.tree.view.get_last_descendant_or_self(first_node_id);
|
||||
Some(last_node_id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,10 +63,9 @@ pub struct MoveToLineOr<A: MovementAction>(pub A);
|
||||
impl<A: MovementAction> MovementAction for MoveToLineOr<A> {
|
||||
fn get_target(&self, layer: &mut FsLayer, environment: &Environment) -> Option<NodeId> where Self: Sized {
|
||||
if let Some(line_number) = layer.registers.count {
|
||||
let tree = &mut layer.tree;
|
||||
let line_index = Some(line_number.saturating_sub(1));
|
||||
let first_node_id = MoveToFirst::get_target(tree);
|
||||
Some(perform_movement_with_count_from(tree, line_index, first_node_id, get_simple_movement_target::<MoveDown>))
|
||||
let first_node_id = MoveToFirst::get_target(layer);
|
||||
Some(perform_movement_with_count_from(layer, line_index, first_node_id, get_simple_movement_target::<MoveDown>))
|
||||
} else {
|
||||
self.0.get_target(layer, environment)
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
use slab_tree::{NodeId, NodeRef};
|
||||
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::component::filesystem::tree::{FsTree, FsTreeViewNode};
|
||||
use crate::component::filesystem::tree::{FsTreeView, FsTreeViewNode};
|
||||
use crate::state::action::{Action, ActionResult};
|
||||
use crate::state::Environment;
|
||||
|
||||
@@ -25,7 +25,7 @@ pub trait MovementAction {
|
||||
impl<T: MovementAction> Action<FsLayer> for T {
|
||||
fn perform(&self, layer: &mut FsLayer, environment: &Environment) -> ActionResult {
|
||||
if let Some(target_node_id) = self.get_target(layer, environment) {
|
||||
layer.tree.selected_view_node_id = target_node_id;
|
||||
layer.selected_view_node_id = target_node_id;
|
||||
ActionResult::Draw
|
||||
} else {
|
||||
ActionResult::Nothing
|
||||
@@ -33,19 +33,15 @@ impl<T: MovementAction> Action<FsLayer> for T {
|
||||
}
|
||||
}
|
||||
|
||||
fn perform_movement_with_count_from_register<F>(layer: &mut FsLayer, get_target: F) -> NodeId where F: Fn(&mut FsTree, NodeId) -> Option<NodeId> {
|
||||
perform_movement_with_count(&mut layer.tree, layer.registers.count, get_target)
|
||||
fn perform_movement_with_count<F>(layer: &mut FsLayer, count: Option<usize>, get_target: F) -> NodeId where F: Fn(&mut FsLayer, NodeId) -> Option<NodeId> {
|
||||
perform_movement_with_count_from(layer, count, layer.selected_view_node_id, get_target)
|
||||
}
|
||||
|
||||
fn perform_movement_with_count<F>(tree: &mut FsTree, count: Option<usize>, get_target: F) -> NodeId where F: Fn(&mut FsTree, NodeId) -> Option<NodeId> {
|
||||
perform_movement_with_count_from(tree, count, tree.selected_view_node_id, get_target)
|
||||
}
|
||||
|
||||
fn perform_movement_with_count_from<F>(tree: &mut FsTree, count: Option<usize>, start_node_id: NodeId, get_target: F) -> NodeId where F: Fn(&mut FsTree, NodeId) -> Option<NodeId> {
|
||||
fn perform_movement_with_count_from<F>(layer: &mut FsLayer, count: Option<usize>, start_node_id: NodeId, get_target: F) -> NodeId where F: Fn(&mut FsLayer, NodeId) -> Option<NodeId> {
|
||||
let mut target_node_id = start_node_id;
|
||||
|
||||
for _ in 0..count.unwrap_or(1) {
|
||||
if let Some(node_id) = get_target(tree, target_node_id) {
|
||||
if let Some(node_id) = get_target(layer, target_node_id) {
|
||||
target_node_id = node_id;
|
||||
} else {
|
||||
break;
|
||||
@@ -56,15 +52,16 @@ fn perform_movement_with_count_from<F>(tree: &mut FsTree, count: Option<usize>,
|
||||
}
|
||||
|
||||
pub trait SimpleMovementAction {
|
||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized;
|
||||
fn get_target(view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized;
|
||||
}
|
||||
|
||||
impl<T: SimpleMovementAction> MovementAction for T {
|
||||
fn get_target(&self, layer: &mut FsLayer, _environment: &Environment) -> Option<NodeId> where Self: Sized {
|
||||
Some(perform_movement_with_count_from_register(layer, get_simple_movement_target::<T>))
|
||||
Some(perform_movement_with_count(layer, layer.registers.count, get_simple_movement_target::<T>))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_simple_movement_target<T: SimpleMovementAction>(tree: &mut FsTree, node_id: NodeId) -> Option<NodeId> {
|
||||
tree.get_view_node(node_id).and_then(|node| T::get_target(&node))
|
||||
fn get_simple_movement_target<T: SimpleMovementAction>(layer: &mut FsLayer, node_id: NodeId) -> Option<NodeId> {
|
||||
let view = &layer.tree.view;
|
||||
view.get(node_id).and_then(|node| T::get_target(view, &node))
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ impl<A: SimpleMovementAction + 'static, C: MovementCount> MovementWithCountFacto
|
||||
impl<A: SimpleMovementAction, C: MovementCount> MovementAction for MovementWithCount<A, C> {
|
||||
fn get_target(&self, layer: &mut FsLayer, environment: &Environment) -> Option<NodeId> where Self: Sized {
|
||||
let count = self.0.get_count(layer.registers.count, environment);
|
||||
Some(perform_movement_with_count(&mut layer.tree, Some(count), get_simple_movement_target::<A>))
|
||||
Some(perform_movement_with_count(layer, Some(count), get_simple_movement_target::<A>))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,7 @@ use std::marker::PhantomData;
|
||||
use slab_tree::{NodeId, NodeRef};
|
||||
|
||||
use crate::component::filesystem::action::movement::SimpleMovementAction;
|
||||
use crate::component::filesystem::tree::FsTreeViewNode;
|
||||
use crate::component::filesystem::tree::{FsTreeView, FsTreeViewNode};
|
||||
|
||||
/// A movement action with fallback.
|
||||
pub struct MovementWithFallback<A: SimpleMovementAction, F: SimpleMovementAction>(PhantomData<A>, PhantomData<F>);
|
||||
@@ -21,7 +21,7 @@ impl<A: SimpleMovementAction, F: SimpleMovementAction> MovementWithFallbackFacto
|
||||
}
|
||||
|
||||
impl<A: SimpleMovementAction, F: SimpleMovementAction> SimpleMovementAction for MovementWithFallback<A, F> {
|
||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
A::get_target(selected_node).or_else(|| F::get_target(selected_node))
|
||||
fn get_target(view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||
A::get_target(view, selected_node).or_else(|| F::get_target(view, selected_node))
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ use slab_tree::NodeId;
|
||||
|
||||
use crate::component::dialog::message::MessageDialogLayer;
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::component::filesystem::tree::FsTree;
|
||||
use crate::state::action::{Action, ActionResult};
|
||||
use crate::state::Environment;
|
||||
|
||||
@@ -20,19 +19,19 @@ impl Action<FsLayer> for ExpandCollapse {
|
||||
return ActionResult::Nothing;
|
||||
}
|
||||
|
||||
if layer.tree.expand_or_collapse(layer.tree.selected_view_node_id) {
|
||||
if layer.expand_or_collapse(layer.selected_view_node_id) {
|
||||
if depth > 1 {
|
||||
if let Some(node) = layer.tree.selected_node() {
|
||||
if let Some(node) = layer.selected_node() {
|
||||
if node.data().is_expanded() {
|
||||
let child_node_ids = node.children().map(|node| node.node_id()).collect();
|
||||
let remaining_depth = depth.saturating_sub(1);
|
||||
if !expand_children_to_depth(&mut layer.tree, child_node_ids, remaining_depth) {
|
||||
return ActionResult::push_layer(MessageDialogLayer::build()
|
||||
if !expand_children_to_depth(layer, child_node_ids, remaining_depth) {
|
||||
return ActionResult::PushLayer(Box::new(MessageDialogLayer::build()
|
||||
.y(layer.dialog_y())
|
||||
.color(Color::LightYellow)
|
||||
.title("Expansion Stopped")
|
||||
.message(format!("Expansion was taking more than {} seconds, stopping now.", MAX_EXPANSION_TIME.as_secs()))
|
||||
.ok());
|
||||
.ok()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +46,7 @@ impl Action<FsLayer> for ExpandCollapse {
|
||||
|
||||
const MAX_EXPANSION_TIME: Duration = Duration::from_secs(10);
|
||||
|
||||
fn expand_children_to_depth(tree: &mut FsTree, mut child_node_ids: Vec<NodeId>, max_depth: usize) -> bool {
|
||||
fn expand_children_to_depth(layer: &mut FsLayer, mut child_node_ids: Vec<NodeId>, max_depth: usize) -> bool {
|
||||
let start_time = Instant::now();
|
||||
let mut current_pass_node_ids = Vec::new();
|
||||
|
||||
@@ -56,8 +55,8 @@ fn expand_children_to_depth(tree: &mut FsTree, mut child_node_ids: Vec<NodeId>,
|
||||
|
||||
for node_id in ¤t_pass_node_ids {
|
||||
let node_id = *node_id;
|
||||
tree.expand(node_id);
|
||||
get_child_node_ids(tree, node_id, &mut child_node_ids);
|
||||
layer.tree.expand(node_id);
|
||||
get_child_node_ids(layer, node_id, &mut child_node_ids);
|
||||
|
||||
if start_time.elapsed() >= MAX_EXPANSION_TIME {
|
||||
return false;
|
||||
@@ -68,8 +67,8 @@ fn expand_children_to_depth(tree: &mut FsTree, mut child_node_ids: Vec<NodeId>,
|
||||
true
|
||||
}
|
||||
|
||||
fn get_child_node_ids(tree: &FsTree, node_id: NodeId, output_node_ids: &mut Vec<NodeId>) {
|
||||
if let Some(node) = tree.get_view_node(node_id) {
|
||||
fn get_child_node_ids(layer: &FsLayer, node_id: NodeId, output_node_ids: &mut Vec<NodeId>) {
|
||||
if let Some(node) = layer.tree.view.get(node_id) {
|
||||
for child in node.children() {
|
||||
output_node_ids.push(child.node_id());
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ pub struct RefreshChildrenOfSelected;
|
||||
|
||||
impl Action<FsLayer> for RefreshChildrenOfSelected {
|
||||
fn perform(&self, layer: &mut FsLayer, _environment: &Environment) -> ActionResult {
|
||||
if layer.tree.refresh_children(layer.tree.selected_view_node_id) {
|
||||
if layer.refresh_children(layer.selected_view_node_id) {
|
||||
ActionResult::Draw
|
||||
} else {
|
||||
ActionResult::Nothing
|
||||
|
@@ -1 +0,0 @@
|
||||
mod shell;
|
@@ -1 +0,0 @@
|
||||
pub struct RunShellCommand;
|
@@ -1,98 +0,0 @@
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::component::filesystem::{ActionKeyMap, FsLayer};
|
||||
use crate::component::filesystem::action::application::{EnterCommandMode, Quit, RedrawScreen};
|
||||
use crate::component::filesystem::action::count::PushCountDigit;
|
||||
use crate::component::filesystem::action::file::{CreateDirectoryInParentOfSelectedEntry, CreateDirectoryInSelectedDirectory, CreateFileInParentOfSelectedEntry, CreateFileInSelectedDirectory, DeleteSelectedEntry, EditSelectedEntry, RenameSelectedEntry};
|
||||
use crate::component::filesystem::action::movement::{CollapseSelectedOr, ExpandSelectedOr, MoveBetweenFirstAndLastSibling, MoveDown, MovementWithCountFactory, MovementWithFallbackFactory, MoveOrTraverseUpParent, MoveToFirst, MoveToLast, MoveToLineOr, MoveToNextSibling, MoveToParent, MoveToPreviousSibling, MoveUp, ScreenHeightRatio};
|
||||
use crate::component::filesystem::action::tree::{ExpandCollapse, RefreshChildrenOfSelected};
|
||||
use crate::input::keymap::KeyMapInsertError;
|
||||
use crate::state::action::Action;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref ACTION_MAP: Result<ActionKeyMap, KeyMapInsertError> = create_action_map();
|
||||
}
|
||||
|
||||
pub fn get_action_map() -> Result<&'static ActionKeyMap, &'static KeyMapInsertError> {
|
||||
return ACTION_MAP.as_ref();
|
||||
}
|
||||
|
||||
fn create_action_map() -> Result<ActionKeyMap, KeyMapInsertError> {
|
||||
let mut me = ActionKeyMap::new();
|
||||
|
||||
map(&mut me, "0", PushCountDigit(0))?;
|
||||
map(&mut me, "1", PushCountDigit(1))?;
|
||||
map(&mut me, "2", PushCountDigit(2))?;
|
||||
map(&mut me, "3", PushCountDigit(3))?;
|
||||
map(&mut me, "4", PushCountDigit(4))?;
|
||||
map(&mut me, "5", PushCountDigit(5))?;
|
||||
map(&mut me, "6", PushCountDigit(6))?;
|
||||
map(&mut me, "7", PushCountDigit(7))?;
|
||||
map(&mut me, "8", PushCountDigit(8))?;
|
||||
map(&mut me, "9", PushCountDigit(9))?;
|
||||
|
||||
map(&mut me, "af", CreateFileInSelectedDirectory)?;
|
||||
map(&mut me, "ad", CreateDirectoryInSelectedDirectory)?;
|
||||
map(&mut me, "e", EditSelectedEntry)?;
|
||||
map(&mut me, "d", DeleteSelectedEntry)?;
|
||||
map(&mut me, "gg", MoveToLineOr(MoveToFirst))?;
|
||||
map(&mut me, "G", MoveToLineOr(MoveToLast))?;
|
||||
map(&mut me, "h", CollapseSelectedOr(MoveToParent))?;
|
||||
map(&mut me, "H", MoveOrTraverseUpParent)?;
|
||||
map(&mut me, "if", CreateFileInSelectedDirectory)?;
|
||||
map(&mut me, "id", CreateDirectoryInSelectedDirectory)?;
|
||||
map(&mut me, "j", MoveDown)?;
|
||||
map(&mut me, "J", MoveToNextSibling.with_fallback(MoveDown))?;
|
||||
map(&mut me, "k", MoveUp)?;
|
||||
map(&mut me, "K", MoveToPreviousSibling.with_fallback(MoveUp))?;
|
||||
map(&mut me, "l", ExpandSelectedOr(MoveDown))?;
|
||||
map(&mut me, "of", CreateFileInParentOfSelectedEntry)?;
|
||||
map(&mut me, "od", CreateDirectoryInParentOfSelectedEntry)?;
|
||||
map(&mut me, "q", Quit)?;
|
||||
map(&mut me, "r", RenameSelectedEntry { prefill: true })?;
|
||||
map(&mut me, "R", RenameSelectedEntry { prefill: false })?;
|
||||
|
||||
map(&mut me, "%", MoveBetweenFirstAndLastSibling)?;
|
||||
map(&mut me, ":", EnterCommandMode)?;
|
||||
|
||||
map(&mut me, "<Ctrl-B>", MoveUp.with_custom_count(ScreenHeightRatio(1)))?;
|
||||
map(&mut me, "<Ctrl-C>", Quit)?;
|
||||
map(&mut me, "<Ctrl-D>", MoveDown.with_default_count(ScreenHeightRatio(2)))?;
|
||||
map(&mut me, "<Ctrl-F>", MoveDown.with_custom_count(ScreenHeightRatio(1)))?;
|
||||
map(&mut me, "<Ctrl-L>", RedrawScreen)?;
|
||||
map(&mut me, "<Ctrl-N>", MoveDown)?;
|
||||
map(&mut me, "<Ctrl-P>", MoveUp)?;
|
||||
map(&mut me, "<Ctrl-U>", MoveUp.with_default_count(ScreenHeightRatio(2)))?;
|
||||
|
||||
map(&mut me, "<Space>", ExpandCollapse { default_depth: 1 })?;
|
||||
map(&mut me, "<Ctrl-Space>", ExpandCollapse { default_depth: 1000 })?;
|
||||
|
||||
map(&mut me, "<Down>", MoveDown)?;
|
||||
map(&mut me, "<Shift-Down>", MoveDown.with_custom_count(ScreenHeightRatio(1)))?;
|
||||
map(&mut me, "<Alt-Down>", MoveToNextSibling.with_fallback(MoveDown))?;
|
||||
|
||||
map(&mut me, "<Up>", MoveUp)?;
|
||||
map(&mut me, "<Shift-Up>", MoveUp.with_custom_count(ScreenHeightRatio(1)))?;
|
||||
map(&mut me, "<Alt-Up>", MoveToPreviousSibling.with_fallback(MoveUp))?;
|
||||
|
||||
map(&mut me, "<Left>", CollapseSelectedOr(MoveToParent))?;
|
||||
map(&mut me, "<Alt-Left>", MoveOrTraverseUpParent)?;
|
||||
|
||||
map(&mut me, "<Right>", ExpandSelectedOr(MoveDown))?;
|
||||
|
||||
map(&mut me, "<Del>", DeleteSelectedEntry)?;
|
||||
|
||||
map(&mut me, "<PageDown>", MoveDown.with_custom_count(ScreenHeightRatio(1)))?;
|
||||
map(&mut me, "<PageUp>", MoveUp.with_custom_count(ScreenHeightRatio(1)))?;
|
||||
|
||||
map(&mut me, "<F2>", RenameSelectedEntry { prefill: true })?;
|
||||
map(&mut me, "<Shift-F2>", RenameSelectedEntry { prefill: false })?;
|
||||
|
||||
map(&mut me, "<F5>", RefreshChildrenOfSelected)?;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
fn map(map: &mut ActionKeyMap, key_binding_str: &str, action: impl Action<FsLayer> + Sync + 'static) -> Result<(), KeyMapInsertError> {
|
||||
map.insert(key_binding_str, Box::new(action))
|
||||
}
|
62
src/component/filesystem/event.rs
Normal file
62
src/component/filesystem/event.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use slab_tree::NodeId;
|
||||
|
||||
use crate::component::filesystem::FsLayer;
|
||||
|
||||
pub type FsLayerPendingEvents = Rc<RefCell<Vec<FsLayerEvent>>>;
|
||||
|
||||
pub enum FsLayerEvent {
|
||||
RefreshViewNodeChildren(NodeId),
|
||||
SelectViewNodeChildByFileName(NodeId, String),
|
||||
DeleteViewNode(NodeId),
|
||||
}
|
||||
|
||||
impl FsLayerEvent {
|
||||
pub fn enqueue(self, pending_events: &FsLayerPendingEvents) -> bool {
|
||||
if let Ok(mut pending_events) = pending_events.try_borrow_mut() {
|
||||
pending_events.push(self);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(self, layer: &mut FsLayer) {
|
||||
match self {
|
||||
Self::RefreshViewNodeChildren(view_node_id) => handle_refresh_view_node_children(layer, view_node_id),
|
||||
Self::SelectViewNodeChildByFileName(parent_view_node_id, child_file_name) => handle_select_view_node_child_by_name(layer, parent_view_node_id, child_file_name.as_str()),
|
||||
Self::DeleteViewNode(view_node_id) => handle_delete_view_node(layer, view_node_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_refresh_view_node_children(layer: &mut FsLayer, view_node_id: NodeId) {
|
||||
layer.refresh_children(view_node_id);
|
||||
}
|
||||
|
||||
fn handle_select_view_node_child_by_name(layer: &mut FsLayer, parent_view_node_id: NodeId, child_file_name: &str) {
|
||||
layer.tree.expand(parent_view_node_id);
|
||||
|
||||
if let Some(parent_node) = layer.tree.view.get(parent_view_node_id) {
|
||||
for child_node in parent_node.children() {
|
||||
if layer.tree.get_entry(&child_node).is_some_and(|entry| entry.name().str() == child_file_name) {
|
||||
layer.selected_view_node_id = child_node.node_id();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_delete_view_node(layer: &mut FsLayer, view_node_id: NodeId) {
|
||||
let view = &mut layer.tree.view;
|
||||
|
||||
if layer.selected_view_node_id == view_node_id {
|
||||
layer.selected_view_node_id = view.get_node_below_id(view_node_id).or_else(|| view.get_node_above_id(view_node_id)).unwrap_or_else(|| view.root_id());
|
||||
}
|
||||
|
||||
if let Some(view_node) = view.remove(view_node_id) {
|
||||
layer.tree.model.remove(view_node.model_node_id());
|
||||
}
|
||||
}
|
@@ -1,65 +1,112 @@
|
||||
use std::cell::RefCell;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
|
||||
use slab_tree::{NodeId, NodeRef};
|
||||
|
||||
use crate::component::filesystem::event::FsLayerPendingEvents;
|
||||
use crate::component::filesystem::registers::FsTreeRegisters;
|
||||
use crate::component::filesystem::tree::FsTree;
|
||||
use crate::component::filesystem::tree::{FsTree, FsTreeViewNode};
|
||||
use crate::file::FileOwnerNameCache;
|
||||
use crate::input::keymap::{KeyBinding, KeyMap, KeyMapLookupResult};
|
||||
use crate::state::action::{Action, ActionResult};
|
||||
use crate::input::keymap::{KeyBinding, KeyMapLookupResult};
|
||||
use crate::state::action::ActionResult;
|
||||
use crate::state::Environment;
|
||||
use crate::state::event::{EventQueue, EventResult};
|
||||
use crate::state::layer::Layer;
|
||||
use crate::state::view::Frame;
|
||||
|
||||
mod action;
|
||||
mod command;
|
||||
mod registers;
|
||||
mod event;
|
||||
mod render;
|
||||
mod tree;
|
||||
pub mod defaults;
|
||||
|
||||
pub type ActionKeyMap = KeyMap<Box<dyn Action<FsLayer> + Sync>>;
|
||||
mod registers;
|
||||
|
||||
pub struct FsLayer {
|
||||
action_map: &'static ActionKeyMap,
|
||||
pub tree: FsTree,
|
||||
tree_structure_version: u32,
|
||||
cursor_y: u16,
|
||||
pub selected_view_node_id: NodeId,
|
||||
pub registers: FsTreeRegisters,
|
||||
cursor_y: u16,
|
||||
pending_keys: Vec<KeyBinding>,
|
||||
event_queue: EventQueue<FsLayer>,
|
||||
pending_events: FsLayerPendingEvents,
|
||||
file_owner_name_cache: FileOwnerNameCache,
|
||||
column_width_cache: Option<ColumnWidths>,
|
||||
}
|
||||
|
||||
impl FsLayer {
|
||||
pub fn new(root_path: &Path, action_map: &'static ActionKeyMap) -> Self {
|
||||
pub fn with_root_path(root_path: &Path) -> Self {
|
||||
// Initialize action map early in case it errors.
|
||||
let _ = *action::ACTION_MAP;
|
||||
|
||||
let mut tree = FsTree::with_root_path(root_path);
|
||||
let root_id = tree.view.root_id();
|
||||
|
||||
tree.expand(root_id);
|
||||
|
||||
Self {
|
||||
action_map,
|
||||
tree: FsTree::with_root_path(root_path),
|
||||
tree_structure_version: 0,
|
||||
tree,
|
||||
selected_view_node_id: root_id,
|
||||
cursor_y: 0,
|
||||
registers: FsTreeRegisters::new(),
|
||||
pending_keys: Vec::new(),
|
||||
event_queue: EventQueue::new(),
|
||||
pending_events: Rc::new(RefCell::new(Vec::new())),
|
||||
file_owner_name_cache: FileOwnerNameCache::new(),
|
||||
column_width_cache: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn events(&self) -> EventQueue<Self> {
|
||||
self.event_queue.rc_clone()
|
||||
}
|
||||
|
||||
pub const fn dialog_y(&self) -> u16 {
|
||||
self.cursor_y.saturating_add(1)
|
||||
}
|
||||
|
||||
pub fn selected_node(&self) -> Option<NodeRef<FsTreeViewNode>> {
|
||||
return self.tree.view.get(self.selected_view_node_id);
|
||||
}
|
||||
|
||||
pub fn expand(&mut self, view_node_id: NodeId) -> bool {
|
||||
let result = self.tree.expand(view_node_id);
|
||||
tree_structure_changed_if_true(self, result)
|
||||
}
|
||||
|
||||
pub fn collapse(&mut self, view_node_id: NodeId) -> bool {
|
||||
let result = self.tree.collapse(view_node_id);
|
||||
tree_structure_changed_if_true(self, result)
|
||||
}
|
||||
|
||||
pub fn expand_or_collapse(&mut self, view_node_id: NodeId) -> bool {
|
||||
let result = self.tree.expand_or_collapse(view_node_id);
|
||||
tree_structure_changed_if_true(self, result)
|
||||
}
|
||||
|
||||
pub fn refresh_children(&mut self, view_node_id: NodeId) -> bool {
|
||||
let result = self.tree.refresh_children(view_node_id);
|
||||
if result && self.selected_node().is_none() {
|
||||
self.selected_view_node_id = view_node_id;
|
||||
}
|
||||
tree_structure_changed_if_true(self, result)
|
||||
}
|
||||
|
||||
pub fn traverse_up_root(&mut self) -> Option<NodeId> {
|
||||
let new_root_id = self.tree.traverse_up_root();
|
||||
tree_structure_changed_if_true(self, new_root_id.is_some());
|
||||
new_root_id
|
||||
}
|
||||
}
|
||||
|
||||
fn tree_structure_changed(layer: &mut FsLayer) {
|
||||
layer.column_width_cache.take();
|
||||
}
|
||||
|
||||
fn tree_structure_changed_if_true(layer: &mut FsLayer, result: bool) -> bool {
|
||||
if result {
|
||||
tree_structure_changed(layer);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
impl Layer for FsLayer {
|
||||
fn handle_input(&mut self, environment: &Environment, key_binding: KeyBinding) -> ActionResult {
|
||||
self.pending_keys.push(key_binding);
|
||||
|
||||
match self.action_map.lookup(&self.pending_keys) {
|
||||
match action::ACTION_MAP.lookup(&self.pending_keys) {
|
||||
KeyMapLookupResult::Prefix => {
|
||||
ActionResult::Nothing
|
||||
}
|
||||
@@ -86,14 +133,9 @@ impl Layer for FsLayer {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_events(&mut self, environment: &Environment) -> EventResult {
|
||||
self.event_queue.take().into_iter().fold(EventResult::Nothing, |result, event| result.merge(event.dispatch(self, environment)))
|
||||
}
|
||||
|
||||
fn render(&mut self, frame: &mut Frame) {
|
||||
if self.tree_structure_version != self.tree.structure_version() {
|
||||
self.tree_structure_version = self.tree.structure_version();
|
||||
self.column_width_cache.take();
|
||||
for event in self.pending_events.take() {
|
||||
event.handle(self);
|
||||
}
|
||||
|
||||
render::render(self, frame);
|
||||
|
@@ -7,7 +7,7 @@ use ratatui::widgets::{Clear, Widget};
|
||||
use slab_tree::{NodeId, NodeRef};
|
||||
|
||||
use crate::component::filesystem::{ColumnWidths, FsLayer};
|
||||
use crate::component::filesystem::tree::{FsTree, FsTreeViewNode};
|
||||
use crate::component::filesystem::tree::{FsTree, FsTreeView, FsTreeViewNode};
|
||||
use crate::file::{FileEntry, FileKind, FileOwnerNameCache};
|
||||
use crate::state::view::Frame;
|
||||
|
||||
@@ -24,7 +24,7 @@ pub fn render(layer: &mut FsLayer, frame: &mut Frame) {
|
||||
let column_widths = get_or_update_column_widths(layer, size.width);
|
||||
let file_owner_name_cache = &mut layer.file_owner_name_cache;
|
||||
|
||||
let (rows, cursor_y) = collect_displayed_rows(&layer.tree, layer.tree.selected_view_node_id, size.height as usize);
|
||||
let (rows, cursor_y) = collect_displayed_rows(&layer.tree, layer.selected_view_node_id, size.height as usize);
|
||||
layer.cursor_y = cursor_y;
|
||||
|
||||
frame.render_widget(Clear, size);
|
||||
@@ -37,7 +37,7 @@ fn get_or_update_column_widths(layer: &mut FsLayer, cols: u16) -> ColumnWidths {
|
||||
let mut user: usize = 0;
|
||||
let mut group: usize = 0;
|
||||
|
||||
for node in layer.tree.view_iter() {
|
||||
for node in &layer.tree.view {
|
||||
let entry = layer.tree.get_entry(&node).unwrap_or_else(|| FileEntry::dummy_as_ref());
|
||||
|
||||
name = max(name, get_node_level(&node).saturating_add(Span::from(entry.name().str()).width()));
|
||||
@@ -63,7 +63,7 @@ fn collect_displayed_rows(tree: &FsTree, selected_node_id: NodeId, terminal_rows
|
||||
let mut displayed_rows = Vec::with_capacity(terminal_rows);
|
||||
let mut cursor_y: u16 = 0;
|
||||
|
||||
if let Some(middle_node) = tree.selected_node().or_else(|| tree.view_root_node()) {
|
||||
if let Some(middle_node) = tree.view.get(selected_node_id).or_else(|| tree.view.root()) {
|
||||
let middle_node_id = middle_node.node_id();
|
||||
|
||||
displayed_rows.push(NodeRow::from(&middle_node, tree, middle_node_id == selected_node_id));
|
||||
@@ -72,7 +72,7 @@ fn collect_displayed_rows(tree: &FsTree, selected_node_id: NodeId, terminal_rows
|
||||
let mut cursor_down_id = Some(middle_node_id);
|
||||
|
||||
while displayed_rows.len() < terminal_rows {
|
||||
if let Some(next_node_up) = move_cursor(tree, &mut cursor_up_id, |node| node.above_id()) {
|
||||
if let Some(next_node_up) = move_cursor(tree, &mut cursor_up_id, FsTreeView::get_node_above) {
|
||||
displayed_rows.insert(0, NodeRow::from(&next_node_up, tree, false));
|
||||
cursor_y = cursor_y.saturating_add(1);
|
||||
}
|
||||
@@ -81,7 +81,7 @@ fn collect_displayed_rows(tree: &FsTree, selected_node_id: NodeId, terminal_rows
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(next_node_down) = move_cursor(tree, &mut cursor_down_id, |node| node.below_id()) {
|
||||
if let Some(next_node_down) = move_cursor(tree, &mut cursor_down_id, FsTreeView::get_node_below) {
|
||||
displayed_rows.push(NodeRow::from(&next_node_down, tree, false));
|
||||
}
|
||||
|
||||
@@ -94,11 +94,12 @@ fn collect_displayed_rows(tree: &FsTree, selected_node_id: NodeId, terminal_rows
|
||||
(displayed_rows, cursor_y)
|
||||
}
|
||||
|
||||
fn move_cursor<'a, F>(tree: &'a FsTree, cursor: &mut Option<NodeId>, func: F) -> Option<NodeRef<'a, FsTreeViewNode>> where F: FnOnce(NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||
fn move_cursor<'a, F>(tree: &'a FsTree, cursor: &mut Option<NodeId>, func: F) -> Option<NodeRef<'a, FsTreeViewNode>> where F: FnOnce(&FsTreeView, &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||
let view = &tree.view;
|
||||
let next_node = cursor
|
||||
.and_then(|id| tree.get_view_node(id))
|
||||
.and_then(func)
|
||||
.and_then(|id| tree.get_view_node(id));
|
||||
.and_then(|id| view.get(id))
|
||||
.and_then(|node| func(view, &node))
|
||||
.and_then(|id| view.get(id));
|
||||
|
||||
*cursor = next_node.as_ref().map(NodeRef::node_id);
|
||||
|
||||
|
@@ -2,7 +2,6 @@ use std::path::Path;
|
||||
|
||||
use slab_tree::{NodeId, NodeRef};
|
||||
|
||||
use crate::component::filesystem::tree::view::FsTreeViewIterator;
|
||||
use crate::file::FileEntry;
|
||||
|
||||
pub use self::model::FsTreeModel;
|
||||
@@ -14,47 +13,16 @@ mod model;
|
||||
mod view;
|
||||
|
||||
pub struct FsTree {
|
||||
model: FsTreeModel,
|
||||
view: FsTreeView,
|
||||
pub selected_view_node_id: NodeId,
|
||||
structure_version: u32,
|
||||
pub model: FsTreeModel,
|
||||
pub view: FsTreeView,
|
||||
}
|
||||
|
||||
impl FsTree {
|
||||
pub fn with_root_path(path: &Path) -> Self {
|
||||
let model = FsTreeModel::with_root_path(path);
|
||||
let view = FsTreeView::from_model_root(&model);
|
||||
let root_id = view.root_id();
|
||||
|
||||
let mut tree = Self {
|
||||
model,
|
||||
view,
|
||||
selected_view_node_id: root_id,
|
||||
structure_version: 0,
|
||||
};
|
||||
|
||||
tree.expand(root_id);
|
||||
tree
|
||||
}
|
||||
|
||||
pub const fn structure_version(&self) -> u32 {
|
||||
self.structure_version
|
||||
}
|
||||
|
||||
pub fn selected_node(&self) -> Option<NodeRef<FsTreeViewNode>> {
|
||||
return self.view.get(self.selected_view_node_id);
|
||||
}
|
||||
|
||||
pub fn view_root_node(&self) -> Option<NodeRef<FsTreeViewNode>> {
|
||||
self.view.root()
|
||||
}
|
||||
|
||||
pub fn view_iter(&self) -> FsTreeViewIterator {
|
||||
self.view.into_iter()
|
||||
}
|
||||
|
||||
pub fn get_view_node(&self, view_node_id: NodeId) -> Option<NodeRef<FsTreeViewNode>> {
|
||||
self.view.get(view_node_id)
|
||||
Self { model, view }
|
||||
}
|
||||
|
||||
pub fn get_entry(&self, node: &NodeRef<FsTreeViewNode>) -> Option<&FileEntry> {
|
||||
@@ -64,79 +32,26 @@ impl FsTree {
|
||||
}
|
||||
|
||||
pub fn expand(&mut self, view_node_id: NodeId) -> bool {
|
||||
let result = self.view.expand(view_node_id, &mut self.model);
|
||||
self.structure_changed_if_true(result)
|
||||
self.view.expand(view_node_id, &mut self.model)
|
||||
}
|
||||
|
||||
pub fn collapse(&mut self, view_node_id: NodeId) -> bool {
|
||||
let result = self.view.collapse(view_node_id);
|
||||
self.structure_changed_if_true(result)
|
||||
self.view.collapse(view_node_id)
|
||||
}
|
||||
|
||||
pub fn expand_or_collapse(&mut self, view_node_id: NodeId) -> bool {
|
||||
let result = self.view.expand_or_collapse(view_node_id, &mut self.model);
|
||||
self.structure_changed_if_true(result)
|
||||
self.view.expand_or_collapse(view_node_id, &mut self.model)
|
||||
}
|
||||
|
||||
pub fn traverse_up_root(&mut self) -> Option<NodeId> {
|
||||
let new_root_id = self.view.traverse_up_root(&mut self.model);
|
||||
self.structure_changed_if(new_root_id, Option::is_some)
|
||||
self.view.traverse_up_root(&mut self.model)
|
||||
}
|
||||
|
||||
pub fn refresh_children(&mut self, view_node_id: NodeId) -> bool {
|
||||
if let Some(view_node) = self.view.get(view_node_id) {
|
||||
let result = self.model.refresh_children(view_node.data().model_node_id()) && self.view.refresh_children(view_node_id, &self.model);
|
||||
if result && self.selected_node().is_none() {
|
||||
self.selected_view_node_id = view_node_id;
|
||||
}
|
||||
self.structure_changed_if_true(result)
|
||||
self.model.refresh_children(view_node.data().model_node_id()) && self.view.refresh_children(view_node_id, &self.model)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_child_node_by_name(&mut self, parent_view_node_id: NodeId, child_file_name: &str) -> bool {
|
||||
self.expand(parent_view_node_id);
|
||||
|
||||
if let Some(parent_node) = self.view.get(parent_view_node_id) {
|
||||
for child_node in parent_node.children() {
|
||||
if self.get_entry(&child_node).is_some_and(|entry| entry.name().str() == child_file_name) {
|
||||
self.selected_view_node_id = child_node.node_id();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn delete_node(&mut self, view_node_id: NodeId) -> bool {
|
||||
let view = &mut self.view;
|
||||
|
||||
if self.selected_view_node_id == view_node_id {
|
||||
self.selected_view_node_id = view.get(view_node_id).and_then(|node| node.below_id().or_else(|| node.above_id())).unwrap_or_else(|| view.root_id());
|
||||
}
|
||||
|
||||
if let Some(view_node) = view.remove(view_node_id) {
|
||||
self.model.remove(view_node.model_node_id());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn structure_changed(&mut self) {
|
||||
self.structure_version = self.structure_version.wrapping_add(1);
|
||||
}
|
||||
|
||||
fn structure_changed_if<T, F>(&mut self, result: T, predicate: F) -> T where F: FnOnce(&T) -> bool {
|
||||
if predicate(&result) {
|
||||
self.structure_changed();
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn structure_changed_if_true(&mut self, result: bool) -> bool {
|
||||
self.structure_changed_if(result, |result| *result)
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ use slab_tree::NodeId;
|
||||
|
||||
use crate::component::filesystem::tree::{FsTreeModel, FsTreeModelNode};
|
||||
use crate::file::FileEntry;
|
||||
use crate::util::slab_tree::{NodeMutExtensions, NodeRefExtensions};
|
||||
|
||||
impl FsTreeModel {
|
||||
pub fn resolve_children(&mut self, node_id: NodeId) -> Option<Vec<NodeId>> {
|
||||
|
48
src/component/filesystem/tree/view/above_below.rs
Normal file
48
src/component/filesystem/tree/view/above_below.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use slab_tree::{NodeId, NodeRef};
|
||||
|
||||
use crate::component::filesystem::tree::{FsTreeView, FsTreeViewNode};
|
||||
use crate::util::slab_tree::NodeRefExtensions;
|
||||
|
||||
impl FsTreeView {
|
||||
pub fn get_node_above(&self, node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||
if let Some(prev_sibling_id) = node.prev_sibling_id() {
|
||||
Some(self.get_last_descendant_or_self(prev_sibling_id))
|
||||
} else {
|
||||
node.parent_id()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn get_node_below(&self, node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||
if let Some(next_id) = node.first_child_id() {
|
||||
Some(next_id)
|
||||
} else if let Some(next_id) = node.next_sibling_id() {
|
||||
Some(next_id)
|
||||
} else {
|
||||
for ancestor in node.ancestors() {
|
||||
if let Some(next_id) = ancestor.next_sibling_id() {
|
||||
return Some(next_id);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_node_above_id(&self, node_id: NodeId) -> Option<NodeId> {
|
||||
self.get(node_id).and_then(|node| self.get_node_above(&node))
|
||||
}
|
||||
|
||||
pub fn get_node_below_id(&self, node_id: NodeId) -> Option<NodeId> {
|
||||
self.get(node_id).and_then(|node| self.get_node_below(&node))
|
||||
}
|
||||
|
||||
pub fn get_last_descendant_or_self(&self, id: NodeId) -> NodeId {
|
||||
let mut id = id;
|
||||
|
||||
while let Some(node_id) = self.get(id).and_then(|node| node.last_child_id()) {
|
||||
id = node_id;
|
||||
}
|
||||
|
||||
id
|
||||
}
|
||||
}
|
@@ -2,8 +2,7 @@ use slab_tree::{NodeId, NodeMut, NodeRef, RemoveBehavior, Tree};
|
||||
|
||||
use crate::component::filesystem::tree::FsTreeModel;
|
||||
|
||||
pub use self::iterator::FsTreeViewIterator;
|
||||
|
||||
mod above_below;
|
||||
mod expand_collapse;
|
||||
mod iterator;
|
||||
mod refresh;
|
||||
|
@@ -4,13 +4,13 @@ use crate::component::filesystem::tree::{FsTreeModel, FsTreeView, FsTreeViewNode
|
||||
|
||||
impl FsTreeView {
|
||||
pub fn traverse_up_root(&mut self, model: &mut FsTreeModel) -> Option<NodeId> {
|
||||
let old_model_root_id = model.root_id();
|
||||
let old_moodel_root_id = model.root_id();
|
||||
|
||||
if let Some(new_model_root_id) = model.traverse_up_root() {
|
||||
self.set_root(new_model_root_id);
|
||||
|
||||
if let Some(mut new_view_root) = self.get_mut(self.root_id) {
|
||||
Self::resolve_new_root_children(&mut new_view_root, model, old_model_root_id, new_model_root_id);
|
||||
Self::resolve_new_root_children(&mut new_view_root, model, old_moodel_root_id, new_model_root_id);
|
||||
Some(self.root_id)
|
||||
} else {
|
||||
None
|
||||
|
@@ -1,16 +1,12 @@
|
||||
use std::cmp::min;
|
||||
|
||||
use crossterm::event::{KeyCode, KeyModifiers};
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::style::{Color, Style};
|
||||
use ratatui::text::Span;
|
||||
use ratatui::widgets::Paragraph;
|
||||
|
||||
use crate::component::input::InputField;
|
||||
use crate::input::keymap::KeyBinding;
|
||||
use crate::state::action::ActionResult;
|
||||
use crate::state::Environment;
|
||||
use crate::state::event::EventResult;
|
||||
use crate::state::layer::Layer;
|
||||
use crate::state::view::Frame;
|
||||
|
||||
@@ -21,10 +17,12 @@ pub struct InputFieldOverlayLayer<'a> {
|
||||
}
|
||||
|
||||
impl<'a> InputFieldOverlayLayer<'a> {
|
||||
pub fn new<F>(read_only_prefix: &'a str, confirm_action: F) -> Self where F: Fn(String) -> ActionResult + 'static {
|
||||
let field = InputField::new();
|
||||
let confirm_action = Box::new(confirm_action);
|
||||
Self { field, read_only_prefix, confirm_action }
|
||||
pub fn new(read_only_prefix: &'a str, confirm_action: Box<dyn Fn(String) -> ActionResult>) -> Self {
|
||||
Self {
|
||||
field: InputField::new(),
|
||||
read_only_prefix,
|
||||
confirm_action,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +53,6 @@ impl<'a> Layer for InputFieldOverlayLayer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_events(&mut self, _environment: &Environment) -> EventResult {
|
||||
EventResult::Nothing
|
||||
}
|
||||
|
||||
fn render(&mut self, frame: &mut Frame) {
|
||||
let size = frame.size();
|
||||
if size.width < 1 || size.height < 1 {
|
||||
@@ -68,22 +62,17 @@ impl<'a> Layer for InputFieldOverlayLayer<'a> {
|
||||
let x = size.x;
|
||||
let y = size.bottom().saturating_sub(1);
|
||||
|
||||
let prefix_text = Span::from(self.read_only_prefix);
|
||||
let prefix_width = min(u16::try_from(prefix_text.width()).unwrap_or(u16::MAX), size.width.saturating_sub(2));
|
||||
let prefix_style = Style::new()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::LightYellow);
|
||||
|
||||
if prefix_width > 0 {
|
||||
let prefix_style = Style::new()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::LightYellow);
|
||||
|
||||
let prefix_paragraph = Paragraph::new(self.read_only_prefix)
|
||||
.style(prefix_style);
|
||||
|
||||
frame.render_widget(prefix_paragraph, Rect { x, y, width: prefix_width, height: 1 });
|
||||
}
|
||||
let prefix_paragraph = Paragraph::new(self.read_only_prefix)
|
||||
.style(prefix_style);
|
||||
|
||||
if size.width > prefix_width {
|
||||
self.field.render(frame, x.saturating_add(prefix_width), y, size.width.saturating_sub(prefix_width), Color::LightYellow, Color::Yellow);
|
||||
frame.render_widget(prefix_paragraph, Rect { x, y, width: 1, height: 1 });
|
||||
|
||||
if size.width > 1 {
|
||||
self.field.render(frame, x.saturating_add(1), y, size.width.saturating_sub(1), Color::LightYellow, Color::Yellow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
57
src/main.rs
57
src/main.rs
@@ -50,13 +50,11 @@
|
||||
#![allow(clippy::redundant_else)]
|
||||
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitCode;
|
||||
|
||||
use crate::state::init::StateInitializer;
|
||||
use crate::state::view::View;
|
||||
|
||||
mod app;
|
||||
mod component;
|
||||
mod file;
|
||||
@@ -67,11 +65,11 @@ mod util;
|
||||
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
fn main() -> ExitCode {
|
||||
fn main() -> Result<ExitCode, Box<dyn Error>> {
|
||||
let args = env::args_os().skip(1).collect::<Vec<_>>();
|
||||
if args.len() == 1 && args.get(0).is_some_and(|arg| arg == "-v" || arg == "--version") {
|
||||
println!("{}", VERSION.unwrap_or("unknown"));
|
||||
return ExitCode::SUCCESS;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
#[allow(clippy::indexing_slicing)] // Guarded by condition.
|
||||
@@ -83,20 +81,21 @@ fn main() -> ExitCode {
|
||||
|
||||
if args.len() > 1 {
|
||||
println!("Too many arguments!");
|
||||
return ExitCode::FAILURE;
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
match get_start_path(args.get(0)) {
|
||||
StartPathResult::Ok(path) => {
|
||||
prepare_and_run_app(&path)
|
||||
app::run(&path)?;
|
||||
Ok(ExitCode::SUCCESS)
|
||||
},
|
||||
StartPathResult::InvalidPathArgument(path) => {
|
||||
println!("Invalid path: {}", path.to_string_lossy());
|
||||
ExitCode::FAILURE
|
||||
Ok(ExitCode::FAILURE)
|
||||
},
|
||||
StartPathResult::InvalidWorkingDirectory => {
|
||||
println!("Invalid working directory!");
|
||||
ExitCode::FAILURE
|
||||
Ok(ExitCode::FAILURE)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,43 +119,3 @@ fn get_start_path(path_arg: Option<&OsString>) -> StartPathResult {
|
||||
StartPathResult::InvalidWorkingDirectory
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
fn prepare_and_run_app(start_path: &Path) -> ExitCode {
|
||||
match component::filesystem::defaults::get_action_map() {
|
||||
Ok(action_map) => {
|
||||
run_app(&StateInitializer {
|
||||
filesystem_start_path: start_path,
|
||||
filesystem_action_map: action_map,
|
||||
})
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Failed to initialize action map, could not insert key sequence: '{}'\nReason: {}", e.sequence(), e.error());
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
fn run_app(state_initializer: &StateInitializer) -> ExitCode {
|
||||
View::restore_terminal_on_panic();
|
||||
|
||||
match View::stdout() {
|
||||
Err(e) => {
|
||||
View::restore_terminal();
|
||||
println!("Failed to initialize terminal: {e}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
Ok(mut view) => {
|
||||
let result = app::run(state_initializer, &mut view);
|
||||
let _ = view.close();
|
||||
|
||||
if let Err(e) = result {
|
||||
println!("Runtime error: {e}");
|
||||
ExitCode::FAILURE
|
||||
} else {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -26,8 +26,4 @@ impl ActionResult {
|
||||
pub fn push_layer<T>(layer: T) -> Self where T: Layer + 'static {
|
||||
Self::PushLayer(Box::new(layer))
|
||||
}
|
||||
|
||||
pub fn replace_layer<T>(layer: T) -> Self where T: Layer + 'static {
|
||||
Self::ReplaceLayer(Box::new(layer))
|
||||
}
|
||||
}
|
||||
|
@@ -1,49 +1,9 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::state::Environment;
|
||||
|
||||
pub trait Event<L> {
|
||||
fn dispatch(&self, layer: &mut L, environment: &Environment) -> EventResult;
|
||||
}
|
||||
|
||||
impl<L, F> Event<L> for F where F: Fn(& mut L, &Environment) -> EventResult {
|
||||
fn dispatch(&self, layer: &mut L, environment: &Environment) -> EventResult {
|
||||
self(layer, environment)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventQueue<L> {
|
||||
events: Rc<RefCell<Vec<Box<dyn Event<L>>>>>
|
||||
}
|
||||
|
||||
impl<L> EventQueue<L> {
|
||||
pub fn new() -> Self {
|
||||
Self { events: Rc::new(RefCell::new(Vec::new())) }
|
||||
}
|
||||
|
||||
pub fn rc_clone(&self) -> Self {
|
||||
Self { events: Rc::clone(&self.events) }
|
||||
}
|
||||
|
||||
pub fn enqueue<E: Event<L> + 'static>(&self, event: E) -> bool {
|
||||
if let Ok(mut events) = self.events.try_borrow_mut() {
|
||||
events.push(Box::new(event));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enqueue_fn<F>(&self, event: F) -> bool where F: Fn(&mut L, &Environment) -> EventResult + 'static {
|
||||
self.enqueue(event)
|
||||
}
|
||||
|
||||
pub fn take(&self) -> Vec<Box<dyn Event<L>>> {
|
||||
self.events.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum EventResult {
|
||||
Nothing,
|
||||
|
@@ -1,8 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::component::filesystem::ActionKeyMap;
|
||||
|
||||
pub struct StateInitializer<'a> {
|
||||
pub filesystem_start_path: &'a Path,
|
||||
pub filesystem_action_map: &'static ActionKeyMap,
|
||||
}
|
@@ -1,11 +1,9 @@
|
||||
use crate::input::keymap::KeyBinding;
|
||||
use crate::state::action::ActionResult;
|
||||
use crate::state::Environment;
|
||||
use crate::state::event::EventResult;
|
||||
use crate::state::view::Frame;
|
||||
|
||||
pub trait Layer {
|
||||
fn handle_input(&mut self, environment: &Environment, key_binding: KeyBinding) -> ActionResult;
|
||||
fn handle_events(&mut self, environment: &Environment) -> EventResult;
|
||||
fn render(&mut self, frame: &mut Frame);
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::component::filesystem::FsLayer;
|
||||
use crate::input::keymap::KeyBinding;
|
||||
use crate::state::action::ActionResult;
|
||||
use crate::state::event::EventResult;
|
||||
use crate::state::init::StateInitializer;
|
||||
use crate::state::layer::Layer;
|
||||
use crate::state::view::Frame;
|
||||
|
||||
@@ -11,7 +11,6 @@ pub use self::environment::Environment;
|
||||
mod environment;
|
||||
pub mod action;
|
||||
pub mod event;
|
||||
pub mod init;
|
||||
pub mod layer;
|
||||
pub mod view;
|
||||
|
||||
@@ -21,19 +20,15 @@ pub struct State {
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(initializer: &StateInitializer, environment: Environment) -> Self {
|
||||
pub fn with_root_path(root_path: &Path, environment: Environment) -> Self {
|
||||
Self {
|
||||
layers: vec![Box::new(FsLayer::new(initializer.filesystem_start_path, initializer.filesystem_action_map))],
|
||||
layers: vec![Box::new(FsLayer::with_root_path(root_path))],
|
||||
environment
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_events(&mut self) -> EventResult {
|
||||
self.layers.iter_mut().fold(EventResult::Nothing, |result, layer| result.merge(layer.handle_events(&self.environment)))
|
||||
}
|
||||
|
||||
pub fn handle_input(&mut self, key_binding: KeyBinding) -> ActionResult {
|
||||
self.layers.last_mut().map_or(ActionResult::Nothing, |layer| layer.handle_input(&self.environment, key_binding))
|
||||
self.layers.last_mut().map(|layer| layer.handle_input(&self.environment, key_binding)).unwrap_or(ActionResult::Nothing)
|
||||
}
|
||||
|
||||
pub fn handle_resize(&mut self, width: u16, height: u16) {
|
||||
|
@@ -10,7 +10,6 @@ use ratatui::widgets::{StatefulWidget, Widget};
|
||||
|
||||
pub struct View {
|
||||
term: Terminal<CrosstermBackend<Stdout>>,
|
||||
render_request: RenderRequest,
|
||||
}
|
||||
|
||||
impl View {
|
||||
@@ -23,22 +22,18 @@ impl View {
|
||||
term.hide_cursor()?;
|
||||
term.clear()?;
|
||||
|
||||
Ok(Self { term, render_request: RenderRequest::Draw })
|
||||
Ok(Self { term })
|
||||
}
|
||||
|
||||
pub fn restore_terminal_on_panic() {
|
||||
let prev_hook = panic::take_hook();
|
||||
|
||||
panic::set_hook(Box::new(move |panic_info| {
|
||||
Self::restore_terminal();
|
||||
let _ = terminal::disable_raw_mode();
|
||||
prev_hook(panic_info);
|
||||
}));
|
||||
}
|
||||
|
||||
pub fn restore_terminal() {
|
||||
let _ = terminal::disable_raw_mode();
|
||||
}
|
||||
|
||||
pub fn close(mut self) -> io::Result<()> {
|
||||
self.term.show_cursor()?;
|
||||
self.term.backend_mut().execute(terminal::LeaveAlternateScreen)?;
|
||||
@@ -50,34 +45,11 @@ impl View {
|
||||
self.term.size()
|
||||
}
|
||||
|
||||
pub fn set_dirty(&mut self, full_redraw: bool) {
|
||||
let new_request = if full_redraw {
|
||||
RenderRequest::Redraw
|
||||
} else {
|
||||
RenderRequest::Draw
|
||||
};
|
||||
|
||||
self.render_request = self.render_request.merge(new_request);
|
||||
pub fn clear(&mut self) -> io::Result<()> {
|
||||
self.term.clear()
|
||||
}
|
||||
|
||||
pub fn render<R>(&mut self, renderer: R) -> io::Result<()> where R: FnOnce(&mut Frame) {
|
||||
match self.render_request.consume() {
|
||||
RenderRequest::Skip => {}
|
||||
|
||||
RenderRequest::Draw => {
|
||||
self.draw(renderer)?;
|
||||
}
|
||||
|
||||
RenderRequest::Redraw => {
|
||||
self.term.clear()?;
|
||||
self.draw(renderer)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw<R>(&mut self, renderer: R) -> io::Result<CompletedFrame> where R: FnOnce(&mut Frame) {
|
||||
pub fn render<R>(&mut self, renderer: R) -> io::Result<CompletedFrame> where R: FnOnce(&mut Frame) {
|
||||
self.term.draw(|frame| {
|
||||
let mut frame = Frame::new(frame);
|
||||
renderer(&mut frame);
|
||||
@@ -86,29 +58,6 @@ impl View {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
enum RenderRequest {
|
||||
Skip,
|
||||
Draw,
|
||||
Redraw,
|
||||
}
|
||||
|
||||
impl RenderRequest {
|
||||
fn merge(self, other: Self) -> Self {
|
||||
if self == Self::Redraw || other == Self::Redraw {
|
||||
Self::Redraw
|
||||
} else if self == Self::Draw || other == Self::Draw {
|
||||
Self::Draw
|
||||
} else {
|
||||
Self::Skip
|
||||
}
|
||||
}
|
||||
|
||||
fn consume(&mut self) -> Self {
|
||||
std::mem::replace(self, Self::Skip)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Frame<'a, 'b> {
|
||||
inner: &'a mut ratatui::Frame<'b, CrosstermBackend<Stdout>>,
|
||||
cursor: Option<(u16, u16)>,
|
||||
|
@@ -1,3 +1,4 @@
|
||||
pub use integer_length::int_len;
|
||||
|
||||
mod integer_length;
|
||||
pub mod slab_tree;
|
||||
|
61
src/util/slab_tree.rs
Normal file
61
src/util/slab_tree.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use slab_tree::{NodeId, NodeMut, NodeRef};
|
||||
|
||||
pub trait NodeRefExtensions {
|
||||
fn parent_id(&self) -> Option<NodeId>;
|
||||
fn first_child_id(&self) -> Option<NodeId>;
|
||||
fn last_child_id(&self) -> Option<NodeId>;
|
||||
fn prev_sibling_id(&self) -> Option<NodeId>;
|
||||
fn next_sibling_id(&self) -> Option<NodeId>;
|
||||
}
|
||||
|
||||
pub trait NodeMutExtensions {
|
||||
fn parent_id(&mut self) -> Option<NodeId>;
|
||||
fn first_child_id(&mut self) -> Option<NodeId>;
|
||||
fn last_child_id(&mut self) -> Option<NodeId>;
|
||||
fn prev_sibling_id(&mut self) -> Option<NodeId>;
|
||||
fn next_sibling_id(&mut self) -> Option<NodeId>;
|
||||
}
|
||||
|
||||
impl<'a, T> NodeRefExtensions for NodeRef<'a, T> {
|
||||
fn parent_id(&self) -> Option<NodeId> {
|
||||
self.parent().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
fn first_child_id(&self) -> Option<NodeId> {
|
||||
self.first_child().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
fn last_child_id(&self) -> Option<NodeId> {
|
||||
self.last_child().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
fn prev_sibling_id(&self) -> Option<NodeId> {
|
||||
self.prev_sibling().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
fn next_sibling_id(&self) -> Option<NodeId> {
|
||||
self.next_sibling().map(|node| node.node_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> NodeMutExtensions for NodeMut<'a, T> {
|
||||
fn parent_id(&mut self) -> Option<NodeId> {
|
||||
self.parent().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
fn first_child_id(&mut self) -> Option<NodeId> {
|
||||
self.first_child().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
fn last_child_id(&mut self) -> Option<NodeId> {
|
||||
self.last_child().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
fn prev_sibling_id(&mut self) -> Option<NodeId> {
|
||||
self.prev_sibling().map(|node| node.node_id())
|
||||
}
|
||||
|
||||
fn next_sibling_id(&mut self) -> Option<NodeId> {
|
||||
self.next_sibling().map(|node| node.node_id())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user