Remove unused files
This commit is contained in:
parent
9ac646174c
commit
baa49107f1
4 changed files with 0 additions and 520 deletions
|
|
@ -1,97 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::FairMutex;
|
||||
use toss::terminal::Terminal;
|
||||
|
||||
use crate::store::{Msg, MsgStore};
|
||||
use crate::ui::util;
|
||||
|
||||
use super::{Cursor, InnerTreeViewState};
|
||||
|
||||
impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||
pub async fn reply_normal(
|
||||
&self,
|
||||
terminal: &mut Terminal,
|
||||
crossterm_lock: &Arc<FairMutex<()>>,
|
||||
) -> Option<(Option<M::Id>, String)> {
|
||||
match &self.cursor {
|
||||
Cursor::Bottom => {
|
||||
if let Some(content) = util::prompt(terminal, crossterm_lock) {
|
||||
return Some((None, content));
|
||||
}
|
||||
}
|
||||
Cursor::Msg(msg) => {
|
||||
let path = self.store.path(msg).await;
|
||||
let tree = self.store.tree(path.first()).await;
|
||||
let parent_id = if tree.next_sibling(msg).is_some() {
|
||||
// A reply to a message that has further siblings should be a
|
||||
// direct reply. An indirect reply might end up a lot further
|
||||
// down in the current conversation.
|
||||
msg.clone()
|
||||
} else if let Some(parent) = tree.parent(msg) {
|
||||
// A reply to a message without younger siblings should be
|
||||
// an indirect reply so as not to create unnecessarily deep
|
||||
// threads. In the case that our message has children, this
|
||||
// might get a bit confusing. I'm not sure yet how well this
|
||||
// "smart" reply actually works in practice.
|
||||
parent
|
||||
} else {
|
||||
// When replying to a top-level message, it makes sense to avoid
|
||||
// creating unnecessary new threads.
|
||||
msg.clone()
|
||||
};
|
||||
|
||||
if let Some(content) = util::prompt(terminal, crossterm_lock) {
|
||||
return Some((Some(parent_id), content));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Does approximately the opposite of [`Self::reply_normal`].
|
||||
pub async fn reply_alternate(
|
||||
&self,
|
||||
terminal: &mut Terminal,
|
||||
crossterm_lock: &Arc<FairMutex<()>>,
|
||||
) -> Option<(Option<M::Id>, String)> {
|
||||
match &self.cursor {
|
||||
Cursor::Bottom => {
|
||||
if let Some(content) = util::prompt(terminal, crossterm_lock) {
|
||||
return Some((None, content));
|
||||
}
|
||||
}
|
||||
Cursor::Msg(msg) => {
|
||||
let path = self.store.path(msg).await;
|
||||
let tree = self.store.tree(path.first()).await;
|
||||
let parent_id = if tree.next_sibling(msg).is_none() {
|
||||
// The opposite of replying normally
|
||||
msg.clone()
|
||||
} else if let Some(parent) = tree.parent(msg) {
|
||||
// The opposite of replying normally
|
||||
parent
|
||||
} else {
|
||||
// The same as replying normally, still to avoid creating
|
||||
// unnecessary new threads
|
||||
msg.clone()
|
||||
};
|
||||
|
||||
if let Some(content) = util::prompt(terminal, crossterm_lock) {
|
||||
return Some((Some(parent_id), content));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn create_new_thread(
|
||||
terminal: &mut Terminal,
|
||||
crossterm_lock: &Arc<FairMutex<()>>,
|
||||
) -> Option<(Option<M::Id>, String)> {
|
||||
util::prompt(terminal, crossterm_lock).map(|content| (None, content))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
//! Intermediate representation of chat history as blocks of things.
|
||||
|
||||
use std::collections::{vec_deque, VecDeque};
|
||||
use std::iter;
|
||||
|
||||
use time::OffsetDateTime;
|
||||
use toss::styled::Styled;
|
||||
|
||||
use crate::macros::some_or_return;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum MarkerBlock<I> {
|
||||
After(I), // TODO Is this marker necessary?
|
||||
Bottom,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MsgContent {
|
||||
Msg { nick: Styled, lines: Vec<Styled> },
|
||||
Placeholder,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MsgBlock<I> {
|
||||
pub id: I,
|
||||
pub content: MsgContent,
|
||||
}
|
||||
|
||||
impl<I> MsgBlock<I> {
|
||||
pub fn height(&self) -> i32 {
|
||||
match &self.content {
|
||||
MsgContent::Msg { lines, .. } => lines.len() as i32,
|
||||
MsgContent::Placeholder => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ComposeBlock {
|
||||
// TODO Editor widget
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BlockBody<I> {
|
||||
Marker(MarkerBlock<I>),
|
||||
Msg(MsgBlock<I>),
|
||||
Compose(ComposeBlock),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Block<I> {
|
||||
pub line: i32,
|
||||
pub time: Option<OffsetDateTime>,
|
||||
pub indent: usize,
|
||||
pub body: BlockBody<I>,
|
||||
}
|
||||
|
||||
impl<I> Block<I> {
|
||||
pub fn bottom(line: i32) -> Self {
|
||||
Self {
|
||||
line,
|
||||
time: None,
|
||||
indent: 0,
|
||||
body: BlockBody::Marker(MarkerBlock::Bottom),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn after(indent: usize, id: I) -> Self {
|
||||
Self {
|
||||
line: 0,
|
||||
time: None,
|
||||
indent,
|
||||
body: BlockBody::Marker(MarkerBlock::After(id)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn msg(
|
||||
time: OffsetDateTime,
|
||||
indent: usize,
|
||||
id: I,
|
||||
nick: Styled,
|
||||
lines: Vec<Styled>,
|
||||
) -> Self {
|
||||
Self {
|
||||
line: 0,
|
||||
time: Some(time),
|
||||
indent,
|
||||
body: BlockBody::Msg(MsgBlock {
|
||||
id,
|
||||
content: MsgContent::Msg { nick, lines },
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn placeholder(time: Option<OffsetDateTime>, indent: usize, id: I) -> Self {
|
||||
Self {
|
||||
line: 0,
|
||||
time,
|
||||
indent,
|
||||
body: BlockBody::Msg(MsgBlock {
|
||||
id,
|
||||
content: MsgContent::Placeholder,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
match &self.body {
|
||||
BlockBody::Marker(m) => 0,
|
||||
BlockBody::Msg(m) => m.height(),
|
||||
BlockBody::Compose(e) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pre-layouted messages as a sequence of blocks.
|
||||
///
|
||||
/// These blocks are straightforward to render, but also provide a level of
|
||||
/// abstraction between the layouting and actual displaying of messages.
|
||||
///
|
||||
/// The following equation describes the relationship between the
|
||||
/// [`Blocks::top_line`] and [`Blocks::bottom_line`] fields:
|
||||
///
|
||||
/// `bottom_line - top_line = sum of all heights - 1`
|
||||
///
|
||||
/// This ensures that `top_line` is always the first line and `bottom_line` is
|
||||
/// always the last line in a nonempty [`Blocks`]. In an empty layout, the
|
||||
/// equation simplifies to
|
||||
///
|
||||
/// `bottom_line = top_line - 1`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Blocks<I> {
|
||||
pub blocks: VecDeque<Block<I>>,
|
||||
/// The top line of the first block. Useful for prepending blocks,
|
||||
/// especially to empty [`Blocks`]s.
|
||||
pub top_line: i32,
|
||||
/// The bottom line of the last block. Useful for appending blocks,
|
||||
/// especially to empty [`Blocks`]s.
|
||||
pub bottom_line: i32,
|
||||
/// The root of the first and last tree, if any. Useful for figuring out
|
||||
/// which blocks to prepend or append.
|
||||
pub roots: Option<(I, I)>,
|
||||
}
|
||||
|
||||
impl<I> Blocks<I> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
blocks: VecDeque::new(),
|
||||
top_line: 1,
|
||||
bottom_line: 0,
|
||||
roots: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_bottom(line: i32) -> Self {
|
||||
Self {
|
||||
blocks: iter::once(Block::bottom(line)).collect(),
|
||||
top_line: line,
|
||||
bottom_line: line - 1,
|
||||
roots: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find<F>(&self, f: F) -> Option<&Block<I>>
|
||||
where
|
||||
F: Fn(&Block<I>) -> bool,
|
||||
{
|
||||
self.blocks.iter().find(|b| f(b))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> vec_deque::Iter<Block<I>> {
|
||||
self.blocks.iter()
|
||||
}
|
||||
|
||||
pub fn update<F>(&mut self, f: F)
|
||||
where
|
||||
F: Fn(&mut Block<I>),
|
||||
{
|
||||
for block in &mut self.blocks {
|
||||
f(block);
|
||||
}
|
||||
}
|
||||
|
||||
fn find_index_and_line<F>(&self, f: F) -> Option<(usize, i32)>
|
||||
where
|
||||
F: Fn(&Block<I>) -> Option<i32>,
|
||||
{
|
||||
self.blocks
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, b)| f(b).map(|l| (i, l)))
|
||||
}
|
||||
|
||||
/// Update the offsets such that the line of the first block with a `Some`
|
||||
/// return value becomes that value.
|
||||
pub fn recalculate_offsets<F>(&mut self, f: F)
|
||||
where
|
||||
F: Fn(&Block<I>) -> Option<i32>,
|
||||
{
|
||||
let (idx, line) = some_or_return!(self.find_index_and_line(f));
|
||||
|
||||
// Propagate lines from index to both ends
|
||||
self.blocks[idx].line = line;
|
||||
for i in (0..idx).rev() {
|
||||
self.blocks[i].line = self.blocks[i + 1].line - self.blocks[i].height();
|
||||
}
|
||||
for i in (idx + 1)..self.blocks.len() {
|
||||
self.blocks[i].line = self.blocks[i - 1].line + self.blocks[i - 1].height();
|
||||
}
|
||||
|
||||
self.top_line = self.blocks.front().expect("blocks nonempty").line;
|
||||
let bottom = self.blocks.back().expect("blocks nonempty");
|
||||
self.bottom_line = bottom.line + bottom.height() - 1;
|
||||
}
|
||||
|
||||
pub fn push_front(&mut self, mut block: Block<I>) {
|
||||
self.top_line -= block.height();
|
||||
block.line = self.top_line;
|
||||
self.blocks.push_front(block);
|
||||
}
|
||||
|
||||
pub fn push_back(&mut self, mut block: Block<I>) {
|
||||
block.line = self.bottom_line + 1;
|
||||
self.bottom_line += block.height();
|
||||
self.blocks.push_back(block);
|
||||
}
|
||||
|
||||
pub fn offset(&mut self, delta: i32) {
|
||||
self.top_line += delta;
|
||||
self.bottom_line += delta;
|
||||
for block in &mut self.blocks {
|
||||
block.line += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Ord> Blocks<I> {
|
||||
pub fn prepend(&mut self, mut layout: Self) {
|
||||
while let Some(block) = layout.blocks.pop_back() {
|
||||
self.push_front(block);
|
||||
}
|
||||
|
||||
if let Some((l_root_top, l_root_bot)) = layout.roots {
|
||||
if let Some((root_top, _)) = &mut self.roots {
|
||||
assert!(l_root_bot < *root_top);
|
||||
*root_top = l_root_top;
|
||||
} else {
|
||||
self.roots = Some((l_root_top, l_root_bot));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append(&mut self, mut layout: Self) {
|
||||
while let Some(block) = layout.blocks.pop_front() {
|
||||
self.push_back(block);
|
||||
}
|
||||
|
||||
if let Some((l_root_top, l_root_bot)) = layout.roots {
|
||||
if let Some((_, root_bot)) = &mut self.roots {
|
||||
assert!(l_root_top > *root_bot);
|
||||
*root_bot = l_root_bot;
|
||||
} else {
|
||||
self.roots = Some((l_root_top, l_root_bot));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
//! Rendering blocks to a [`Frame`].
|
||||
|
||||
use time::OffsetDateTime;
|
||||
use toss::frame::{Frame, Pos};
|
||||
use toss::styled::Styled;
|
||||
|
||||
use crate::store::{Msg, MsgStore};
|
||||
|
||||
use super::blocks::{Block, BlockBody, MsgBlock, MsgContent};
|
||||
use super::{util, InnerTreeViewState};
|
||||
|
||||
impl<M: Msg, S: MsgStore<M>> InnerTreeViewState<M, S> {
|
||||
fn render_time(frame: &mut Frame, line: i32, time: Option<OffsetDateTime>, is_cursor: bool) {
|
||||
let pos = Pos::new(0, line);
|
||||
let style = if is_cursor {
|
||||
util::style_time_inverted()
|
||||
} else {
|
||||
util::style_time()
|
||||
};
|
||||
|
||||
if let Some(time) = time {
|
||||
let time = time
|
||||
.format(util::TIME_FORMAT)
|
||||
.expect("time can be formatted");
|
||||
frame.write(pos, (&time, style));
|
||||
} else {
|
||||
frame.write(pos, (util::TIME_EMPTY, style));
|
||||
}
|
||||
}
|
||||
|
||||
fn render_indent(frame: &mut Frame, line: i32, indent: usize, is_cursor: bool) {
|
||||
let pos = Pos::new(util::after_indent(0), line);
|
||||
let style = if is_cursor {
|
||||
util::style_indent_inverted()
|
||||
} else {
|
||||
util::style_indent()
|
||||
};
|
||||
|
||||
let mut styled = Styled::default();
|
||||
for _ in 0..indent {
|
||||
styled = styled.then((util::INDENT, style));
|
||||
}
|
||||
|
||||
frame.write(pos, styled);
|
||||
}
|
||||
|
||||
fn render_nick(frame: &mut Frame, line: i32, indent: usize, nick: Styled) {
|
||||
let nick_pos = Pos::new(util::after_indent(indent), line);
|
||||
let styled = Styled::new("[").and_then(nick).then("]");
|
||||
frame.write(nick_pos, styled);
|
||||
}
|
||||
|
||||
fn draw_msg_block(
|
||||
frame: &mut Frame,
|
||||
line: i32,
|
||||
time: Option<OffsetDateTime>,
|
||||
indent: usize,
|
||||
msg: &MsgBlock<M::Id>,
|
||||
is_cursor: bool,
|
||||
) {
|
||||
match &msg.content {
|
||||
MsgContent::Msg { nick, lines } => {
|
||||
let height: i32 = frame.size().height.into();
|
||||
let after_nick = util::after_nick(frame, indent, nick);
|
||||
|
||||
for (i, text) in lines.iter().enumerate() {
|
||||
let line = line + i as i32;
|
||||
if line < 0 || line >= height {
|
||||
continue;
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
Self::render_indent(frame, line, indent, is_cursor);
|
||||
Self::render_time(frame, line, time, is_cursor);
|
||||
Self::render_nick(frame, line, indent, nick.clone());
|
||||
} else {
|
||||
Self::render_indent(frame, line, indent + 1, false);
|
||||
Self::render_indent(frame, line, indent, is_cursor);
|
||||
Self::render_time(frame, line, None, is_cursor);
|
||||
}
|
||||
|
||||
frame.write(Pos::new(after_nick, line), text.clone());
|
||||
}
|
||||
}
|
||||
MsgContent::Placeholder => {
|
||||
Self::render_time(frame, line, time, is_cursor);
|
||||
Self::render_indent(frame, line, indent, is_cursor);
|
||||
let pos = Pos::new(util::after_indent(indent), line);
|
||||
frame.write(pos, (util::PLACEHOLDER, util::style_placeholder()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_block(frame: &mut Frame, block: &Block<M::Id>, is_cursor: bool) {
|
||||
match &block.body {
|
||||
BlockBody::Marker(_) => {}
|
||||
BlockBody::Msg(msg) => {
|
||||
Self::draw_msg_block(frame, block.line, block.time, block.indent, msg, is_cursor)
|
||||
}
|
||||
BlockBody::Compose(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_blocks(&self, frame: &mut Frame) {
|
||||
for block in self.last_blocks.iter() {
|
||||
Self::draw_block(frame, block, self.cursor.matches_block(block));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
//! Constants and helper functions.
|
||||
|
||||
use crossterm::style::{ContentStyle, Stylize};
|
||||
use time::format_description::FormatItem;
|
||||
use time::macros::format_description;
|
||||
use toss::frame::Frame;
|
||||
use toss::styled::Styled;
|
||||
|
||||
pub const TIME_FORMAT: &[FormatItem<'_>] =
|
||||
format_description!("[year]-[month]-[day] [hour]:[minute] ");
|
||||
pub const TIME_EMPTY: &str = " ";
|
||||
pub const TIME_WIDTH: usize = TIME_EMPTY.len();
|
||||
|
||||
pub fn style_time() -> ContentStyle {
|
||||
ContentStyle::default().grey()
|
||||
}
|
||||
|
||||
pub fn style_time_inverted() -> ContentStyle {
|
||||
ContentStyle::default().black().on_white()
|
||||
}
|
||||
|
||||
pub const INDENT: &str = "│ ";
|
||||
pub const INDENT_WIDTH: usize = 2;
|
||||
|
||||
pub fn style_indent() -> ContentStyle {
|
||||
ContentStyle::default().dark_grey()
|
||||
}
|
||||
|
||||
pub fn style_indent_inverted() -> ContentStyle {
|
||||
ContentStyle::default().black().on_white()
|
||||
}
|
||||
|
||||
pub const PLACEHOLDER: &str = "[...]";
|
||||
|
||||
pub fn style_placeholder() -> ContentStyle {
|
||||
ContentStyle::default().dark_grey()
|
||||
}
|
||||
|
||||
pub const MIN_CONTENT_WIDTH: usize = "[+, 1234 more]".len();
|
||||
|
||||
pub fn after_indent(indent: usize) -> i32 {
|
||||
(TIME_WIDTH + indent * INDENT_WIDTH) as i32
|
||||
}
|
||||
|
||||
pub fn after_nick(frame: &mut Frame, indent: usize, nick: &Styled) -> i32 {
|
||||
after_indent(indent) + 1 + frame.width_styled(nick) as i32 + 2
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue