aboutsummaryrefslogtreecommitdiffstats
path: root/src/term.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/term.rs')
-rw-r--r--src/term.rs417
1 files changed, 0 insertions, 417 deletions
diff --git a/src/term.rs b/src/term.rs
index 3561521..02c21bb 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -1,11 +1,5 @@
use crate::prelude::*;
-const RESET: &[&[u8]] = &[b"\x1bc"];
-const CLEAR: &[&[u8]] =
- &[b"\x1b[3J\x1b[H\x1b[2J", b"\x1b[H\x1b[J", b"\x1b[H\x1b[2J"];
-const WINDOW_TITLE: &[(&[u8], &[u8])] =
- &[(b"\x1b]0;", b"\x07"), (b"\x1b]2;", b"\x07")];
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Size {
pub rows: u16,
@@ -29,414 +23,3 @@ impl std::fmt::Display for Size {
std::fmt::Display::fmt(&format!("{}x{}", self.cols, self.rows), f)
}
}
-
-#[derive(Debug, Clone, Default)]
-pub struct Buffer {
- max_size: usize,
- contents: Vec<u8>,
- title: String,
-}
-
-impl Buffer {
- pub fn new(max_size: usize) -> Self {
- let mut self_ = Self::default();
- self_.max_size = max_size;
- self_
- }
-
- // doesn't parse out window titles, since we don't care about them. also
- // tries to spread out truncations a bit to keep each individual run of
- // this function reasonably fast (trying to avoid most runs being fast but
- // occasionally one being extra slow when it hits a limit). this makes it
- // a bit less accurate (clears don't actually reset the entire terminal
- // state, so dropping everything before them when we see one isn't quite
- // correct), but the client side buffer is only used in the case where
- // someone reconnects after disconnecting from the server, which should be
- // uncommon enough that it shouldn't matter all that much.
- pub fn append_client(&mut self, buf: &[u8], written: usize) -> usize {
- self.contents.extend_from_slice(buf);
-
- if written == 0 {
- return written;
- }
-
- if self.find_reset(buf).is_some() {
- self.truncate_at(written);
- return written;
- }
-
- if self.find_clear(buf).is_some() {
- self.truncate_at(written);
- return written;
- }
-
- if self.contents.len() > self.max_size {
- let to_drop = self.contents.len() - self.max_size / 2;
- let to_drop = to_drop.min(written);
- if to_drop > 0 {
- self.truncate_at(to_drop);
- return to_drop;
- }
- }
-
- 0
- }
-
- // aim for accuracy over speed - on the client side, we're guaranteed to
- // see every byte because the bytes are coming from the client, but our
- // truncation behavior isn't particularly guaranteed to give correct
- // results in all cases, so try to avoid doing it too much if we can help
- // it. this makes this function slower (and spikier) than append_client,
- // but that's pretty okay because we don't care as much about latency
- // here.
- pub fn append_server(&mut self, buf: &[u8]) {
- self.contents.extend_from_slice(buf);
-
- if let Some(title) = self.find_window_title(buf) {
- self.title = title;
- }
-
- // resets are actually safe to truncate at, because they reset ~all
- // terminal state
- if let Some(i) = self.find_reset(buf) {
- self.truncate_at(self.contents.len() - buf.len() + i);
- }
-
- while self.contents.len() > self.max_size {
- let hard_truncate = self.contents.len() - self.max_size / 2;
- // off by one so that we skip over a clear escape sequence at the
- // start of the buffer
- if let Some(i) = self.find_clear(&self.contents[1..]) {
- self.truncate_at((i + 1).min(hard_truncate));
- } else {
- self.truncate_at(hard_truncate);
- }
- }
- }
-
- pub fn contents(&self) -> &[u8] {
- &self.contents
- }
-
- pub fn len(&self) -> usize {
- self.contents.len()
- }
-
- pub fn title(&self) -> &str {
- &self.title
- }
-
- fn find_window_title(&self, buf: &[u8]) -> Option<String> {
- let mut found = None;
- for window_title in WINDOW_TITLE {
- if let Some(i) = twoway::rfind_bytes(buf, window_title.0) {
- if let Some(j) = twoway::find_bytes(&buf[i..], window_title.1)
- {
- let start = i + window_title.0.len();
- let end = j + i;
- if let Ok(title) = std::str::from_utf8(&buf[start..end]) {
- if let Some((i2, _)) = found {
- if i > i2 {
- found = Some((i, title));
- }
- } else {
- found = Some((i, title));
- }
- }
- }
- }
- }
- found.map(|(_, title)| title.to_string())
- }
-
- fn find_clear(&self, buf: &[u8]) -> Option<usize> {
- for clear in CLEAR {
- if let Some(i) = twoway::find_bytes(buf, clear) {
- return Some(i);
- }
- }
- None
- }
-
- fn find_reset(&self, buf: &[u8]) -> Option<usize> {
- for reset in RESET {
- // rfind because we only ever care about the most recent reset,
- // unlike clears where we might want to be more conservative
- if let Some(i) = twoway::rfind_bytes(buf, reset) {
- return Some(i);
- }
- }
- None
- }
-
- fn truncate_at(&mut self, i: usize) {
- let new_contents = self.contents.split_off(i);
- self.contents = new_contents;
- }
-}
-
-#[cfg(test)]
-#[allow(clippy::cognitive_complexity)]
-#[allow(clippy::redundant_clone)]
-#[allow(clippy::shadow_unrelated)]
-mod test {
- use super::*;
-
- #[test]
- fn test_basic() {
- let mut buffer = Buffer::new(100);
- assert_eq!(buffer.contents(), b"");
- assert_eq!(buffer.len(), 0);
- assert_eq!(buffer.title(), "");
-
- let n = buffer.append_client(b"foo", 0);
- assert_eq!(buffer.contents(), b"foo");
- assert_eq!(buffer.len(), 3);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client(b"bar", 3);
- assert_eq!(buffer.contents(), b"foobar");
- assert_eq!(buffer.len(), 6);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
- }
-
- #[test]
- fn test_clear() {
- let mut buffer = Buffer::new(100);
-
- let n = buffer.append_client(b"foo", 0);
- assert_eq!(buffer.contents(), b"foo");
- assert_eq!(buffer.len(), 3);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client(b"\x1b[3J\x1b[H\x1b[2J", 3);
- assert_eq!(buffer.contents(), b"\x1b[3J\x1b[H\x1b[2J");
- assert_eq!(buffer.len(), 11);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 3);
-
- let n = buffer.append_client(b"bar", 11);
- assert_eq!(buffer.contents(), b"\x1b[3J\x1b[H\x1b[2Jbar");
- assert_eq!(buffer.len(), 14);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client(b"baz\x1bcquux", 14);
- assert_eq!(buffer.contents(), b"baz\x1bcquux");
- assert_eq!(buffer.len(), 9);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 14);
-
- let n = buffer.append_client(b"blorg\x1b[H\x1b[J", 9);
- assert_eq!(buffer.contents(), b"blorg\x1b[H\x1b[J");
- assert_eq!(buffer.len(), 11);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 9);
-
- let n = buffer.append_client(b"\x1b[H\x1b[2Jabc", 11);
- assert_eq!(buffer.contents(), b"\x1b[H\x1b[2Jabc");
- assert_eq!(buffer.len(), 10);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 11);
-
- let n =
- buffer.append_client(b"first\x1bcsecond\x1b[H\x1b[2Jthird", 10);
- assert_eq!(buffer.contents(), b"first\x1bcsecond\x1b[H\x1b[2Jthird");
- assert_eq!(buffer.len(), 25);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 10);
-
- let n =
- buffer.append_client(b"first\x1b[H\x1b[2Jsecond\x1bcthird", 25);
- assert_eq!(buffer.contents(), b"first\x1b[H\x1b[2Jsecond\x1bcthird");
- assert_eq!(buffer.len(), 25);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 25);
- }
-
- #[test]
- fn test_title() {
- let mut buffer = Buffer::new(50);
-
- buffer.append_server(b"\x1b]0;this is a title\x07");
- assert_eq!(buffer.contents(), b"\x1b]0;this is a title\x07");
- assert_eq!(buffer.len(), 20);
- assert_eq!(buffer.title(), "this is a title");
-
- buffer.append_server(b"\x1b]2;this is another title\x07");
- assert_eq!(
- buffer.contents(),
- &b"\x1b]0;this is a title\x07\x1b]2;this is another title\x07"[..]
- );
- assert_eq!(buffer.len(), 46);
- assert_eq!(buffer.title(), "this is another title");
-
- buffer.append_server(b"\x1bcfoo");
- assert_eq!(buffer.contents(), &b"\x1bcfoo"[..]);
- assert_eq!(buffer.len(), 5);
- assert_eq!(buffer.title(), "this is another title");
-
- buffer.append_server(
- b"\x1bcabc\x1b]0;title1\x07def\x1b]2;title2\x07ghi",
- );
- assert_eq!(
- buffer.contents(),
- &b"\x1bcabc\x1b]0;title1\x07def\x1b]2;title2\x07ghi"[..]
- );
- assert_eq!(buffer.len(), 33);
- assert_eq!(buffer.title(), "title2");
-
- buffer.append_server(
- b"\x1bcabc\x1b]2;title3\x07def\x1b]0;title4\x07ghi",
- );
- assert_eq!(
- buffer.contents(),
- &b"\x1bcabc\x1b]2;title3\x07def\x1b]0;title4\x07ghi"[..]
- );
- assert_eq!(buffer.len(), 33);
- assert_eq!(buffer.title(), "title4");
- }
-
- #[test]
- fn test_size_limit_client() {
- let mut buffer = Buffer::new(100);
-
- let n = buffer.append_client(b"foobarbazq", 0);
- assert_eq!(buffer.contents(), b"foobarbazq");
- assert_eq!(buffer.len(), 10);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client("0123456789".repeat(9).as_ref(), 10);
- assert_eq!(
- buffer.contents(),
- &b"foobarbazq012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"[..]
- );
- assert_eq!(buffer.len(), 100);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let n = buffer.append_client(b"z", 100);
- assert_eq!(
- buffer.contents(),
- &b"1234567890123456789012345678901234567890123456789z"[..]
- );
- assert_eq!(buffer.len(), 50);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 51);
-
- let n = buffer.append_client("abcdefghij".repeat(15).as_ref(), 50);
- assert_eq!(
- buffer.contents(),
- &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]
- );
- assert_eq!(buffer.len(), 150);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 50);
- }
-
- #[test]
- fn test_written() {
- let mut buffer = Buffer::new(100);
-
- let n = buffer.append_client("abcdefghij".repeat(15).as_ref(), 0);
- assert_eq!(
- buffer.contents(),
- &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]
- );
- assert_eq!(buffer.len(), 150);
- assert_eq!(buffer.title(), "");
- assert_eq!(n, 0);
-
- let mut buffer2 = buffer.clone();
- let n = buffer2.append_client(b"z", 0);
- assert_eq!(
- buffer2.contents(),
- &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..]
- );
- assert_eq!(buffer2.len(), 151);
- assert_eq!(buffer2.title(), "");
- assert_eq!(n, 0);
-
- let mut buffer2 = buffer.clone();
- let n = buffer2.append_client(b"z", 20);
- assert_eq!(
- buffer2.contents(),
- &b"abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..]
- );
- assert_eq!(buffer2.len(), 131);
- assert_eq!(buffer2.title(), "");
- assert_eq!(n, 20);
-
- let mut buffer2 = buffer.clone();
- let n = buffer2.append_client(b"z", 130);
- assert_eq!(
- buffer2.contents(),
- &b"bcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..]
- );
- assert_eq!(buffer2.len(), 50);
- assert_eq!(buffer2.title(), "");
- assert_eq!(n, 101);
- }
-
- #[test]
- fn test_server() {
- let mut buffer = Buffer::new(100);
-
- buffer.append_server(b"foobar");
- assert_eq!(buffer.contents(), b"foobar");
- assert_eq!(buffer.len(), 6);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"\x1b[3J\x1b[H\x1b[2J");
- assert_eq!(buffer.contents(), b"foobar\x1b[3J\x1b[H\x1b[2J");
- assert_eq!(buffer.len(), 17);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server("abcdefghij".repeat(8).as_ref());
- assert_eq!(buffer.contents(), &b"foobar\x1b[3J\x1b[H\x1b[2Jabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]);
- assert_eq!(buffer.len(), 97);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"abcd");
- assert_eq!(buffer.contents(), &b"\x1b[3J\x1b[H\x1b[2Jabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcd"[..]);
- assert_eq!(buffer.len(), 95);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server("\x1b[H\x1b[Jfooo".repeat(8).as_ref());
- assert_eq!(buffer.contents(), &b"\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo"[..]);
- assert_eq!(buffer.len(), 80);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server("abcdefghij".repeat(5).as_ref());
- assert_eq!(buffer.contents(), &b"\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfoooabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"[..]);
- assert_eq!(buffer.len(), 100);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"z");
- assert_eq!(buffer.contents(), &b"\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfoooabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijz"[..]);
- assert_eq!(buffer.len(), 91);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"bcdefghijabcdefghij\x1b[H\x1b[Jfooo");
- assert_eq!(buffer.contents(), &b"\x1b[H\x1b[Jfooo\x1b[H\x1b[Jfoooabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijzbcdefghijabcdefghij\x1b[H\x1b[Jfooo"[..]);
- assert_eq!(buffer.len(), 100);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server(b"abcdefghijz");
- assert_eq!(
- buffer.contents(),
- &b"bcdefghijzbcdefghijabcdefghij\x1b[H\x1b[Jfoooabcdefghijz"[..]
- );
- assert_eq!(buffer.len(), 50);
- assert_eq!(buffer.title(), "");
-
- buffer.append_server("\x1bcfooobaar".repeat(8).as_ref());
- assert_eq!(buffer.contents(), b"\x1bcfooobaar");
- assert_eq!(buffer.len(), 10);
- assert_eq!(buffer.title(), "");
- }
-}