Fix ThreadListing

ThreadListing was broken after the ThreadGroup introduction
memfd
Manos Pitsidianakis 2020-02-23 15:01:37 +02:00
parent 68007a0842
commit 45c0160cb6
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
3 changed files with 97 additions and 166 deletions

View File

@ -272,13 +272,13 @@ impl FromStr for SortOrder {
#[derive(Default, Clone, Debug, Deserialize, Serialize)]
pub struct Thread {
root: ThreadNodeHash,
date: UnixTimestamp,
len: usize,
unseen: usize,
attachments: usize,
pub root: ThreadNodeHash,
pub date: UnixTimestamp,
pub len: usize,
pub unseen: usize,
pub attachments: usize,
snoozed: bool,
pub snoozed: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -294,7 +294,7 @@ impl Default for ThreadGroup {
}
impl ThreadGroup {
fn root(&self) -> Option<&Thread> {
pub fn root(&self) -> Option<&Thread> {
if let ThreadGroup::Root(ref root) = self {
Some(root)
} else {
@ -498,21 +498,14 @@ impl Threads {
}
}
pub fn threads_iter(&self) -> ThreadsIterator {
ThreadsIterator {
pub fn threads_group_iter(
&self,
root_tree: SmallVec<[ThreadNodeHash; 1024]>,
) -> ThreadsGroupIterator {
ThreadsGroupIterator {
root_tree,
pos: 0,
stack: SmallVec::new(),
root_tree: self.tree_index.borrow(),
thread_nodes: &self.thread_nodes,
}
}
pub fn thread_iter(&self, index: usize) -> ThreadIterator {
ThreadIterator {
init_pos: index,
pos: index,
stack: SmallVec::new(),
root_tree: self.tree_index.borrow(),
thread_nodes: &self.thread_nodes,
}
}
@ -1191,14 +1184,6 @@ impl Threads {
.filter_map(|(h, g)| g.root().map(|_| *h))
.collect::<SmallVec<[ThreadHash; 1024]>>()
}
pub fn root_iter(&self) -> RootIterator {
RootIterator {
pos: 0,
root_tree: self.tree_index.borrow(),
thread_nodes: &self.thread_nodes,
}
}
}
impl Index<&ThreadNodeHash> for Threads {

View File

@ -22,7 +22,6 @@
use super::{ThreadNode, ThreadNodeHash};
use fnv::FnvHashMap;
use smallvec::SmallVec;
use std::cell::Ref;
/* `ThreadsIterator` returns messages according to the sorted order. For example, for the following
* threads:
@ -39,13 +38,13 @@ use std::cell::Ref;
* the iterator returns them as `A, B, C, D, E, F`
*/
pub struct ThreadsIterator<'a> {
pub struct ThreadsGroupIterator<'a> {
pub(super) root_tree: SmallVec<[ThreadNodeHash; 1024]>,
pub(super) pos: usize,
pub(super) stack: SmallVec<[usize; 16]>,
pub(super) root_tree: Ref<'a, Vec<ThreadNodeHash>>,
pub(super) thread_nodes: &'a FnvHashMap<ThreadNodeHash, ThreadNode>,
}
impl<'a> Iterator for ThreadsIterator<'a> {
impl<'a> Iterator for ThreadsGroupIterator<'a> {
type Item = (usize, ThreadNodeHash, bool);
fn next(&mut self) -> Option<(usize, ThreadNodeHash, bool)> {
{
@ -97,75 +96,6 @@ impl<'a> Iterator for ThreadsIterator<'a> {
* the iterator returns them as `A, B, C, D`
*/
pub struct ThreadIterator<'a> {
pub(super) init_pos: usize,
pub(super) pos: usize,
pub(super) stack: SmallVec<[usize; 16]>,
pub(super) root_tree: Ref<'a, Vec<ThreadNodeHash>>,
pub(super) thread_nodes: &'a FnvHashMap<ThreadNodeHash, ThreadNode>,
}
impl<'a> Iterator for ThreadIterator<'a> {
type Item = (usize, ThreadNodeHash);
fn next(&mut self) -> Option<(usize, ThreadNodeHash)> {
{
let mut tree = &(*self.root_tree);
for i in self.stack.iter() {
tree = &self.thread_nodes[&tree[*i]].children;
}
if self.pos == tree.len() || (self.stack.is_empty() && self.pos > self.init_pos) {
if self.stack.is_empty() {
return None;
}
self.pos = self.stack.pop().unwrap() + 1;
} else {
debug_assert!(self.pos < tree.len());
let ret = (self.stack.len(), tree[self.pos]);
if !self.thread_nodes[&tree[self.pos]].children.is_empty() {
self.stack.push(self.pos);
self.pos = 0;
if self.thread_nodes[&ret.1].message.is_some() {
return Some(ret);
} else {
return self.next();
}
}
self.pos += 1;
if self.thread_nodes[&ret.1].message.is_some() {
return Some(ret);
}
}
}
self.next()
}
}
pub struct RootIterator<'a> {
pub pos: usize,
pub root_tree: Ref<'a, Vec<ThreadNodeHash>>,
pub thread_nodes: &'a FnvHashMap<ThreadNodeHash, ThreadNode>,
}
impl<'a> Iterator for RootIterator<'a> {
type Item = ThreadNodeHash;
fn next(&mut self) -> Option<ThreadNodeHash> {
{
if self.pos == self.root_tree.len() {
return None;
}
let mut ret = self.root_tree[self.pos];
self.pos += 1;
let thread_node = &self.thread_nodes[&ret];
if thread_node.message().is_none() {
ret = thread_node.children()[0];
while self.thread_nodes[&ret].message().is_none() {
ret = self.thread_nodes[&ret].children()[0];
}
}
Some(ret)
}
}
}
pub struct ThreadGroupIterator<'a> {
pub(super) group: ThreadNodeHash,
pub(super) pos: usize,

View File

@ -39,7 +39,7 @@ pub struct ThreadListing {
color_cache: ColorCache,
row_updates: SmallVec<[ThreadHash; 8]>,
locations: Vec<EnvelopeHash>,
order: FnvHashMap<EnvelopeHash, usize>,
/// If we must redraw on next redraw event
dirty: bool,
/// If `self.view` is focused or not.
@ -117,8 +117,8 @@ impl MailListingTrait for ThreadListing {
}
let account = &context.accounts[self.cursor_pos.0];
let threads = &account.collection.threads[&self.cursor_pos.1];
self.length = threads.len();
self.locations.clear();
self.length = 0;
self.order.clear();
let default_cell = {
let mut ret = Cell::with_char(' ');
ret.set_fg(self.color_cache.theme_default.fg)
@ -126,7 +126,7 @@ impl MailListingTrait for ThreadListing {
.set_attrs(self.color_cache.theme_default.attrs);
ret
};
if self.length == 0 {
if threads.len() == 0 {
let message = format!("Folder `{}` is empty.", account[&self.cursor_pos.1].name());
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
write_string_to_grid(
@ -141,14 +141,19 @@ impl MailListingTrait for ThreadListing {
return;
}
self.content =
CellBuffer::new_with_context(MAX_COLS, self.length + 1, default_cell, context);
CellBuffer::new_with_context(MAX_COLS, threads.len() + 1, default_cell, context);
let mut indentations: Vec<bool> = Vec::with_capacity(6);
let mut thread_idx = 0; // needed for alternate thread colors
/* Draw threaded view. */
threads.sort_by(self.sort, self.subsort, &account.collection.envelopes);
let mut roots = threads.roots();
threads.group_inner_sort_by(&mut roots, self.sort, &account.collection.envelopes);
let roots = roots
.into_iter()
.filter_map(|r| threads.groups[&r].root().map(|r| r.root))
.collect::<_>();
let mut iter = threads.threads_group_iter(roots).peekable();
let thread_nodes: &FnvHashMap<ThreadNodeHash, ThreadNode> = &threads.thread_nodes();
let mut iter = threads.threads_iter().peekable();
/* This is just a desugared for loop so that we can use .peek() */
let mut idx = 0;
while let Some((indentation, thread_node_hash, has_sibling)) = iter.next() {
@ -160,7 +165,7 @@ impl MailListingTrait for ThreadListing {
if thread_node.has_message() {
let envelope: EnvelopeRef =
account.collection.get_env(thread_node.message().unwrap());
self.locations.push(envelope.hash());
self.order.insert(envelope.hash(), idx);
let fg_color = if !envelope.is_seen() {
Color::Byte(0)
} else {
@ -216,6 +221,7 @@ impl MailListingTrait for ThreadListing {
_ => {}
}
}
self.length = self.order.len();
}
}
@ -227,7 +233,7 @@ impl ListingTrait for ThreadListing {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.unfocused = false;
self.view = None;
self.locations.clear();
self.order.clear();
self.row_updates.clear();
self.initialised = false;
}
@ -240,7 +246,12 @@ impl ListingTrait for ThreadListing {
let bottom_right = bottom_right!(area);
if self.length == 0 {
clear_area(grid, area, self.color_cache.theme_default);
copy_area(grid, &self.content, area, ((0, 0), (MAX_COLS - 1, 0)));
copy_area(
grid,
&self.content,
area,
((0, 0), pos_dec(self.content.size(), (1, 1))),
);
context.dirty_areas.push_back(area);
return;
}
@ -346,31 +357,30 @@ impl ListingTrait for ThreadListing {
}
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
if context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1].is_empty() {
if self.length == 0 {
return;
}
if self.locations[idx] != 0 {
let envelope: EnvelopeRef = context.accounts[self.cursor_pos.0]
.collection
.get_env(self.locations[idx]);
let env_hash = self.get_env_under_cursor(idx, context);
let envelope: EnvelopeRef = context.accounts[self.cursor_pos.0]
.collection
.get_env(env_hash);
let fg_color = if !envelope.is_seen() {
Color::Byte(0)
} else {
Color::Default
};
let bg_color = if self.cursor_pos.2 == idx {
Color::Byte(246)
} else if !envelope.is_seen() {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(236)
} else {
Color::Default
};
change_colors(grid, area, fg_color, bg_color);
}
let fg_color = if !envelope.is_seen() {
Color::Byte(0)
} else {
Color::Default
};
let bg_color = if self.cursor_pos.2 == idx {
Color::Byte(246)
} else if !envelope.is_seen() {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(236)
} else {
Color::Default
};
change_colors(grid, area, fg_color, bg_color);
}
fn set_movement(&mut self, mvm: PageMovement) {
@ -396,7 +406,7 @@ impl ThreadListing {
content: CellBuffer::new(0, 0, Cell::with_char(' ')),
color_cache: ColorCache::default(),
row_updates: SmallVec::new(),
locations: Vec::new(),
order: FnvHashMap::default(),
dirty: true,
unfocused: false,
view: None,
@ -407,33 +417,33 @@ impl ThreadListing {
}
fn highlight_line_self(&mut self, idx: usize, context: &Context) {
if context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1].is_empty() {
if self.length == 0 {
return;
}
if self.locations[idx] != 0 {
let envelope: EnvelopeRef = context.accounts[self.cursor_pos.0]
.collection
.get_env(self.locations[idx]);
let fg_color = if !envelope.is_seen() {
Color::Byte(0)
} else {
Color::Default
};
let bg_color = if !envelope.is_seen() {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(236)
} else {
Color::Default
};
change_colors(
&mut self.content,
((0, idx), (MAX_COLS - 1, idx)),
fg_color,
bg_color,
);
}
let env_hash = self.get_env_under_cursor(idx, context);
let envelope: EnvelopeRef = context.accounts[self.cursor_pos.0]
.collection
.get_env(env_hash);
let fg_color = if !envelope.is_seen() {
Color::Byte(0)
} else {
Color::Default
};
let bg_color = if !envelope.is_seen() {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(236)
} else {
Color::Default
};
change_colors(
&mut self.content,
((0, idx), (MAX_COLS - 1, idx)),
fg_color,
bg_color,
);
}
fn make_thread_entry(
@ -483,6 +493,19 @@ impl ThreadListing {
}
s
}
fn get_env_under_cursor(&self, cursor: usize, _context: &Context) -> EnvelopeHash {
*self
.order
.iter()
.find(|(_, &r)| r == cursor)
.unwrap_or_else(|| {
debug!("self.order empty ? cursor={} {:#?}", cursor, &self.order);
panic!();
})
.0
}
fn format_date(envelope: &Envelope) -> String {
let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date());
let now: std::time::Duration = std::time::SystemTime::now()
@ -531,13 +554,6 @@ impl Component for ThreadListing {
let idx = self.cursor_pos.2;
let has_message: bool = self.locations[self.new_cursor_pos.2] > 0;
if !has_message {
self.dirty = false;
/* Draw the entire list */
return self.draw_list(grid, area, context);
}
/* Mark message as read */
let must_highlight = {
if self.length == 0 {
@ -546,7 +562,7 @@ impl Component for ThreadListing {
let account = &context.accounts[self.cursor_pos.0];
let envelope: EnvelopeRef = account
.collection
.get_env(self.locations[self.cursor_pos.2]);
.get_env(self.get_env_under_cursor(idx, context));
envelope.is_seen()
}
};
@ -594,7 +610,7 @@ impl Component for ThreadListing {
let coordinates = (
self.cursor_pos.0,
self.cursor_pos.1,
self.locations[self.cursor_pos.2],
self.get_env_under_cursor(self.cursor_pos.2, context),
);
if let Some(ref mut v) = self.view {