Make buffers return their items lazily from the end instead of materializing them
This commit is contained in:
@ -24,7 +24,7 @@ use tokio::sync::mpsc::UnboundedReceiver;
|
|||||||
use tracing_error::ErrorLayer;
|
use tracing_error::ErrorLayer;
|
||||||
use tracing_subscriber::prelude::*;
|
use tracing_subscriber::prelude::*;
|
||||||
|
|
||||||
use super::Buffer;
|
use super::{Buffer, BufferItem};
|
||||||
|
|
||||||
/// Maximum number of log lines to be stored in memory
|
/// Maximum number of log lines to be stored in memory
|
||||||
const MAX_MEM_LOG_LINES: usize = 100;
|
const MAX_MEM_LOG_LINES: usize = 100;
|
||||||
@ -61,19 +61,21 @@ impl Buffer for LogBuffer {
|
|||||||
self.lines.push_back(line);
|
self.lines.push_back(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content(&self) -> Vec<Text> {
|
fn content<'a>(&'a self) -> Box<dyn Iterator<Item = BufferItem<'a>> + 'a> {
|
||||||
use ansi_to_tui::IntoText;
|
use ansi_to_tui::IntoText;
|
||||||
let (slice1, slice2) = self.lines.as_slices();
|
let (slice1, slice2) = self.lines.as_slices();
|
||||||
slice1
|
Box::new(
|
||||||
.into_iter()
|
slice1
|
||||||
.chain(slice2.into_iter())
|
.into_iter()
|
||||||
.cloned()
|
.chain(slice2.into_iter())
|
||||||
.map(|line| {
|
.rev()
|
||||||
line.into_text().unwrap_or_else(|e| {
|
.cloned()
|
||||||
tracing::error!("Could not convert line from ANSI codes to ratatui: {}", e);
|
.map(|line| BufferItem {
|
||||||
Text::raw(line)
|
text: line.into_text().unwrap_or_else(|e| {
|
||||||
})
|
tracing::error!("Could not convert line from ANSI codes to ratatui: {}", e);
|
||||||
})
|
Text::raw(line)
|
||||||
.collect()
|
}),
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,12 +18,17 @@ use futures::stream::FuturesUnordered;
|
|||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use matrix_sdk::async_trait;
|
use matrix_sdk::async_trait;
|
||||||
use nonempty::NonEmpty;
|
use nonempty::NonEmpty;
|
||||||
|
use ratatui::text::Text;
|
||||||
|
|
||||||
mod log;
|
mod log;
|
||||||
pub use log::LogBuffer;
|
pub use log::LogBuffer;
|
||||||
mod room;
|
mod room;
|
||||||
pub use room::RoomBuffer;
|
pub use room::RoomBuffer;
|
||||||
|
|
||||||
|
pub struct BufferItem<'a> {
|
||||||
|
pub text: Text<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Buffer: Send + Sync {
|
pub trait Buffer: Send + Sync {
|
||||||
/// A short human-readable name for the room, eg. to show in compact buflist
|
/// A short human-readable name for the room, eg. to show in compact buflist
|
||||||
@ -34,7 +39,7 @@ pub trait Buffer: Send + Sync {
|
|||||||
}
|
}
|
||||||
/// Returns if there are any updates to apply.
|
/// Returns if there are any updates to apply.
|
||||||
async fn poll_updates(&mut self);
|
async fn poll_updates(&mut self);
|
||||||
fn content(&self) -> Vec<ratatui::text::Text>; // TODO: make this lazy, only the last few are used
|
fn content<'a>(&'a self) -> Box<dyn Iterator<Item = BufferItem<'a>> + 'a>;
|
||||||
/// Called when the user is being showned the oldest items this buffer returned.
|
/// Called when the user is being showned the oldest items this buffer returned.
|
||||||
///
|
///
|
||||||
/// This should return immediately, not waiting for anything to be loaded.
|
/// This should return immediately, not waiting for anything to be loaded.
|
||||||
|
@ -32,7 +32,7 @@ use matrix_sdk_ui::timeline::{
|
|||||||
use ratatui::text::Text;
|
use ratatui::text::Text;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use super::Buffer;
|
use super::{Buffer, BufferItem};
|
||||||
|
|
||||||
pub struct SingleClientRoomBuffer {
|
pub struct SingleClientRoomBuffer {
|
||||||
room_id: OwnedRoomId,
|
room_id: OwnedRoomId,
|
||||||
@ -304,16 +304,20 @@ impl Buffer for RoomBuffer {
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content(&self) -> Vec<Text> {
|
fn content<'a>(&'a self) -> Box<dyn Iterator<Item = BufferItem<'a>> + 'a> {
|
||||||
// TODO: merge buffers, etc.
|
// TODO: merge buffers, etc.
|
||||||
self
|
Box::new(
|
||||||
.buffers
|
self
|
||||||
.first()
|
.buffers
|
||||||
.unwrap_or_else(|| panic!("No sub-buffer for {}", self.room_id))
|
.first()
|
||||||
.items
|
.unwrap_or_else(|| panic!("No sub-buffer for {}", self.room_id))
|
||||||
.iter()
|
.items
|
||||||
.map(|line| Text::raw(line))
|
.iter()
|
||||||
.collect()
|
.rev()
|
||||||
|
.map(|line| BufferItem {
|
||||||
|
text: Text::raw(line),
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_back_pagination(&self, num: u16) {
|
fn request_back_pagination(&self, num: u16) {
|
||||||
|
@ -64,8 +64,6 @@ impl Component for Backlog {
|
|||||||
|
|
||||||
let active_buffer = buffers.active_buffer();
|
let active_buffer = buffers.active_buffer();
|
||||||
let mut items = active_buffer.content();
|
let mut items = active_buffer.content();
|
||||||
items.reverse();
|
|
||||||
let mut items = items.into_iter();
|
|
||||||
let mut scroll = self.scroll;
|
let mut scroll = self.scroll;
|
||||||
|
|
||||||
// Skip widgets at the bottom (if scrolled up), and render the first visible one
|
// Skip widgets at the bottom (if scrolled up), and render the first visible one
|
||||||
@ -73,7 +71,7 @@ impl Component for Backlog {
|
|||||||
let Some(item) = items.next() else {
|
let Some(item) = items.next() else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
let widget = BottomAlignedParagraph::new(item);
|
let widget = BottomAlignedParagraph::new(item.text);
|
||||||
let expected_height = widget.height(text_area.width);
|
let expected_height = widget.height(text_area.width);
|
||||||
|
|
||||||
if scroll.saturating_sub(expected_height) > text_area.height.into() {
|
if scroll.saturating_sub(expected_height) > text_area.height.into() {
|
||||||
@ -95,7 +93,7 @@ impl Component for Backlog {
|
|||||||
|
|
||||||
// Render other widgets
|
// Render other widgets
|
||||||
for item in items {
|
for item in items {
|
||||||
let widget = BottomAlignedParagraph::new(item);
|
let widget = BottomAlignedParagraph::new(item.text);
|
||||||
let height = widget.render_overlap(text_area, frame.buffer_mut());
|
let height = widget.render_overlap(text_area, frame.buffer_mut());
|
||||||
assert!(area.height >= height, "{:?} {}", area, height);
|
assert!(area.height >= height, "{:?} {}", area, height);
|
||||||
text_area.height = text_area.height.saturating_sub(height); // Remove lines at the bottom used by this paragraph
|
text_area.height = text_area.height.saturating_sub(height); // Remove lines at the bottom used by this paragraph
|
||||||
|
Reference in New Issue
Block a user