Trigger re-render when pushing to the log

This commit is contained in:
2023-11-04 16:38:46 +01:00
parent 0d24d659c5
commit 6f8c3a19b2
4 changed files with 42 additions and 39 deletions

View File

@ -17,7 +17,6 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::RwLock;
use color_eyre::eyre::{eyre, Result, WrapErr}; use color_eyre::eyre::{eyre, Result, WrapErr};
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
@ -56,7 +55,7 @@ impl App {
pub async fn new( pub async fn new(
tick_rate: f64, tick_rate: f64,
frame_rate: f64, frame_rate: f64,
mem_log: &'static RwLock<VecDeque<String>>, log_receiver: mpsc::UnboundedReceiver<String>,
) -> Result<Self> { ) -> Result<Self> {
let home = Home::new(); let home = Home::new();
let fps = FpsCounter::default(); let fps = FpsCounter::default();
@ -132,7 +131,7 @@ impl App {
components: vec![Box::new(home), Box::new(fps)], components: vec![Box::new(home), Box::new(fps)],
should_quit: AtomicBool::new(false), should_quit: AtomicBool::new(false),
should_suspend: false, should_suspend: false,
buffers: Buffers::new(Box::new(LogBuffer::new(mem_log))), buffers: Buffers::new(Box::new(LogBuffer::new(log_receiver))),
clients, clients,
config, config,
commands: RataCommands::new(), commands: RataCommands::new(),

View File

@ -18,34 +18,52 @@ use std::collections::VecDeque;
use std::sync::Arc; use std::sync::Arc;
use std::sync::RwLock; use std::sync::RwLock;
use matrix_sdk::async_trait;
use ratatui::text::Text; use ratatui::text::Text;
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;
/// Maximum number of log lines to be stored in memory
const MAX_MEM_LOG_LINES: usize = 100;
pub struct LogBuffer { pub struct LogBuffer {
lines: &'static RwLock<VecDeque<String>>, lines: VecDeque<String>,
receiver: UnboundedReceiver<String>,
} }
impl LogBuffer { impl LogBuffer {
pub fn new(lines: &'static RwLock<VecDeque<String>>) -> Self { pub fn new(receiver: UnboundedReceiver<String>) -> Self {
LogBuffer { lines } LogBuffer {
lines: VecDeque::new(),
receiver,
}
} }
} }
#[async_trait]
impl Buffer for LogBuffer { impl Buffer for LogBuffer {
fn short_name(&self) -> String { fn short_name(&self) -> String {
"ratatrix".to_owned() "ratatrix".to_owned()
} }
async fn poll_updates(&mut self) {
let line = self
.receiver
.recv()
.await
.expect("LogBuffer's channel was closed");
if self.lines.len() >= MAX_MEM_LOG_LINES {
self.lines.pop_front();
}
self.lines.push_back(line);
}
fn content(&self) -> Vec<Text> { fn content(&self) -> Vec<Text> {
use ansi_to_tui::IntoText; use ansi_to_tui::IntoText;
let lines = self let (slice1, slice2) = self.lines.as_slices();
.lines
.read()
.expect("LogBuffer could not get log's RwLock as it is poisoned");
let (slice1, slice2) = lines.as_slices();
slice1 slice1
.into_iter() .into_iter()
.chain(slice2.into_iter()) .chain(slice2.into_iter())

View File

@ -29,9 +29,7 @@ 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
fn short_name(&self) -> String; fn short_name(&self) -> String;
/// 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);
std::future::pending().await
}
fn content(&self) -> Vec<ratatui::text::Text>; // TODO: make this lazy, only the last few are used fn content(&self) -> Vec<ratatui::text::Text>; // TODO: make this lazy, only the last few are used
/// 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.
/// ///

View File

@ -18,23 +18,21 @@ use std::collections::VecDeque;
use std::sync::RwLock; use std::sync::RwLock;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use tracing_error::ErrorLayer; use tracing_error::ErrorLayer;
use tracing_subscriber::prelude::*; use tracing_subscriber::prelude::*;
use crate::utils::{get_data_dir, LOG_ENV, LOG_FILE}; use crate::utils::{get_data_dir, LOG_ENV, LOG_FILE};
/// Maximum number of log lines to be stored in memory
const MAX_MEM_LOG_LINES: usize = 100;
pub struct LogWriter { pub struct LogWriter {
lines: &'static RwLock<VecDeque<String>>, sender: UnboundedSender<String>,
last_line: Vec<u8>, last_line: Vec<u8>,
} }
impl LogWriter { impl LogWriter {
fn new(lines: &'static RwLock<VecDeque<String>>) -> Self { fn new(sender: UnboundedSender<String>) -> Self {
LogWriter { LogWriter {
lines, sender,
last_line: Vec::new(), last_line: Vec::new(),
} }
} }
@ -55,20 +53,12 @@ impl std::io::Write for LogWriter {
// No new line, nothing to do // No new line, nothing to do
} else { } else {
let last_line = new_lines.pop().expect("Split returned empty vec").to_vec(); let last_line = new_lines.pop().expect("Split returned empty vec").to_vec();
let mut lines = self.lines.write().map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::Other,
format!(
"LogWriter could not get log's RwLock as it is poisoned: {}",
e
),
)
})?;
for new_line in new_lines.into_iter() { for new_line in new_lines.into_iter() {
if lines.len() >= MAX_MEM_LOG_LINES { let new_line = String::from_utf8_lossy(new_line).to_string();
lines.pop_front(); self
} .sender
lines.push_back(String::from_utf8_lossy(new_line).to_string()); .send(new_line)
.unwrap_or_else(|e| panic!("Could not push log line: {:?}", e));
} }
self.last_line.clear(); self.last_line.clear();
self.last_line.extend(&last_line[..]); self.last_line.extend(&last_line[..]);
@ -78,7 +68,7 @@ impl std::io::Write for LogWriter {
} }
} }
pub fn initialize_logging() -> Result<&'static RwLock<VecDeque<String>>> { pub fn initialize_logging() -> Result<UnboundedReceiver<String>> {
let directory = get_data_dir(); let directory = get_data_dir();
std::fs::create_dir_all(directory.clone())?; std::fs::create_dir_all(directory.clone())?;
let log_path = directory.join(LOG_FILE.clone()); let log_path = directory.join(LOG_FILE.clone());
@ -98,15 +88,13 @@ pub fn initialize_logging() -> Result<&'static RwLock<VecDeque<String>>> {
.with_filter(tracing_subscriber::filter::EnvFilter::from_default_env()); .with_filter(tracing_subscriber::filter::EnvFilter::from_default_env());
// We keep the log until the application stops, so we might as well return it as &'static // We keep the log until the application stops, so we might as well return it as &'static
let lines = Box::leak(Box::new(RwLock::new(VecDeque::with_capacity( let (sender, receiver) = unbounded_channel();
MAX_MEM_LOG_LINES,
))));
let mem_subscriber = tracing_subscriber::fmt::layer() let mem_subscriber = tracing_subscriber::fmt::layer()
.with_ansi(true) .with_ansi(true)
.with_file(true) .with_file(true)
.with_line_number(true) .with_line_number(true)
.with_target(false) .with_target(false)
.with_writer(|| LogWriter::new(lines)) .with_writer(move || LogWriter::new(sender.clone()))
.with_filter(tracing_subscriber::filter::EnvFilter::from_default_env()); .with_filter(tracing_subscriber::filter::EnvFilter::from_default_env());
tracing_subscriber::registry() tracing_subscriber::registry()
@ -114,5 +102,5 @@ pub fn initialize_logging() -> Result<&'static RwLock<VecDeque<String>>> {
.with(file_subscriber) .with(file_subscriber)
.with(ErrorLayer::default()) .with(ErrorLayer::default())
.init(); .init();
Ok(lines) Ok(receiver)
} }