Repository: osa1/tiny (language: rust)

Issue:
Is there a max buffer size?
Nice client!

I'm using tiny on a multi-user system in tmux, and i want to make sure I don't use an unbounded amount of memory if I just leave it open all day.


Discussion / hints:
Thanks for the feedback!

We currently don't have this. This is actually quite easy to implement, the only reason why I haven't done so so far is because even though I leave my client running too (but not for months, in the worst case for a week or two, I think) I never see it using more than 0.1% or 0.2% of memory on my system.

For leaving it running for months on a multi-user system I agree that it might be useful and even necessary.

We can also easily make this configurable (e.g. max lines to keep in a tab).

Marking as "feature request". If anyone wants to implement I'd be happy to help. If not then I might do this myself this weekend.

Fix patch:
diff --git a/libtiny_tui/src/config.rs b/libtiny_tui/src/config.rs
index c96a5c7b..fb52f5da 100644
--- a/libtiny_tui/src/config.rs
+++ b/libtiny_tui/src/config.rs
@@ -13,6 +13,8 @@ pub use termbox_simple::*;
 pub(crate) struct Config {
     #[serde(default)]
     pub(crate) colors: Colors,
+    #[serde(default = "usize::max_value")]
+    pub(crate) scrollback: usize,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
diff --git a/libtiny_tui/src/messaging.rs b/libtiny_tui/src/messaging.rs
index b9c0fae1..64ada7f1 100644
--- a/libtiny_tui/src/messaging.rs
+++ b/libtiny_tui/src/messaging.rs
@@ -70,9 +70,9 @@ struct ActivityLine {
 }
 
 impl MessagingUI {
-    pub(crate) fn new(width: i32, height: i32, status: bool) -> MessagingUI {
+    pub(crate) fn new(width: i32, height: i32, status: bool, scrollback: usize) -> MessagingUI {
         MessagingUI {
-            msg_area: MsgArea::new(width, height - 1),
+            msg_area: MsgArea::new(width, height - 1, scrollback),
             input_field: InputArea::new(width, get_input_field_max_height(height)),
             exit_dialogue: None,
             width,
diff --git a/libtiny_tui/src/msg_area/mod.rs b/libtiny_tui/src/msg_area/mod.rs
index f72787d2..e3a41c40 100644
--- a/libtiny_tui/src/msg_area/mod.rs
+++ b/libtiny_tui/src/msg_area/mod.rs
@@ -1,14 +1,15 @@
 pub(crate) mod line;
 
+use std::collections::VecDeque;
 use std::{cmp::max, mem, str};
-
 use termbox_simple::Termbox;
 
 pub(crate) use self::line::{Line, SegStyle};
 use crate::config::Colors;
 
 pub(crate) struct MsgArea {
-    lines: Vec<Line>,
+    lines: VecDeque<Line>,
+    scrollback: usize,
 
     // Rendering related
     width: i32,
@@ -27,9 +28,10 @@ pub(crate) struct MsgArea {
 }
 
 impl MsgArea {
-    pub(crate) fn new(width: i32, height: i32) -> MsgArea {
+    pub(crate) fn new(width: i32, height: i32, scrollback: usize) -> MsgArea {
         MsgArea {
-            lines: Vec::new(),
+            lines: VecDeque::with_capacity(512.min(scrollback)),
+            scrollback,
             width,
             height,
             scroll: 0,
@@ -157,8 +159,13 @@ impl MsgArea {
 
     pub(crate) fn flush_line(&mut self) -> usize {
         let line_height = self.line_buf.rendered_height(self.width);
+        // Check if we're about to overflow
+        if self.lines.len() == self.scrollback {
+            // Remove oldest line
+            self.lines.pop_front();
+        }
         self.lines
-            .push(mem::replace(&mut self.line_buf, Line::new()));
+            .push_back(mem::replace(&mut self.line_buf, Line::new()));
         if self.scroll != 0 {
             self.scroll += line_height;
         }
@@ -189,7 +196,7 @@ mod tests {
 
     #[test]
     fn newline_scrolling() {
-        let mut msg_area = MsgArea::new(100, 1);
+        let mut msg_area = MsgArea::new(100, 1, usize::MAX);
         // Adding a new line when scroll is 0 should not change it
         assert_eq!(msg_area.scroll, 0);
         msg_area.add_text("line1");
@@ -206,4 +213,21 @@ mod tests {
         msg_area.flush_line();
         assert_eq!(msg_area.scroll, 2);
     }
+
+    #[test]
+    fn test_max_lines() {
+        // Can't show more than 3 lines.
+        let mut msg_area = MsgArea::new(100, 1, 3);
+        msg_area.add_text("first");
+        msg_area.flush_line();
+        msg_area.add_text("second");
+        msg_area.flush_line();
+        msg_area.add_text("third");
+        msg_area.flush_line();
+        assert_eq!(msg_area.lines.len(), 3);
+        msg_area.add_text("fourth");
+        // Will pop out "first" line
+        msg_area.flush_line();
+        assert_eq!(msg_area.lines.len(), 3);
+    }
 }
diff --git a/libtiny_tui/src/tui.rs b/libtiny_tui/src/tui.rs
index 6357ae59..df077460 100644
--- a/libtiny_tui/src/tui.rs
+++ b/libtiny_tui/src/tui.rs
@@ -50,6 +50,9 @@ pub(crate) struct TUI {
     /// Color scheme
     colors: Colors,
 
+    /// Max number of message lines
+    scrollback: usize,
+
     tabs: Vec<Tab>,
     active_idx: usize,
     width: i32,
@@ -91,6 +94,7 @@ impl TUI {
         let mut tui = TUI {
             tb,
             colors: Colors::default(),
+            scrollback: usize::MAX,
             tabs: Vec::new(),
             active_idx: 0,
             width,
@@ -227,7 +231,10 @@ impl TUI {
                         &MsgTarget::CurrentTab,
                     );
                 }
-                Ok(Config { colors }) => self.set_colors(colors),
+                Ok(Config { colors, scrollback }) => {
+                    self.set_colors(colors);
+                    self.scrollback = scrollback.max(1);
+                }
             }
         }
     }
@@ -291,7 +298,12 @@ impl TUI {
             idx,
             Tab {
                 alias,
-                widget: MessagingUI::new(self.width, self.height - 1 - statusline_height, status),
+                widget: MessagingUI::new(
+                    self.width,
+                    self.height - 1 - statusline_height,
+                    status,
+                    self.scrollback,
+                ),
                 src,
                 style: TabStyle::Normal,
                 switch,
diff --git a/tiny/config.yml b/tiny/config.yml
index 8cd8240e..5b5fbefd 100644
--- a/tiny/config.yml
+++ b/tiny/config.yml
@@ -39,6 +39,9 @@ defaults:
 # Where to put log files
 log_dir: '{}'
 
+# (Optional) Limits the maximum number of messages stored for each UI tab. Defaults to unlimited.
+# scrollback: 512
+
 # Color theme based on 256 colors. Colors can be defined as color indices
 # (0-255) or with their names.
 #


Test patch: