Rendering buffer in stdout using crossterm

⚓ Rust    📅 2025-05-15    👤 surdeus    👁️ 4      

surdeus

Warning

This post was published 97 days ago. The information described in this article may have changed.
pub fn draw(&self) -> anyhow::Result<()> {
    let mut stdout = self.stdout.borrow_mut();
    let mut lines = self.text.lines_at(self.y_offset);

    eprintln!("first line: {:#?}", lines.clone().next());
    eprintln!("size.1: {}", self.size.1);
    
    for i in 0..self.size.1 - 1 {
        let line = lines.next().unwrap_or("".into());
        let str = format!("{line:<width$}", width = self.size.0 as usize);

        if i < 3 {
            eprintln!("Line {}: len={}, content={:?}", i, str.len(), str);
        }

        stdout.queue(cursor::MoveTo(0, i))?.queue(Print(str))?;
    }

    stdout
        .queue(cursor::MoveTo(0, 40))?
        .queue(Print("AAAAAAAAAAAAAAAAAAA"))?;

    let cursor = self.cursor.head();
    stdout.queue(cursor::MoveTo(cursor.0, cursor.1))?;
    stdout.flush()?;

    Ok(())
}

Here's snippet of function used to render a visible part of buffer into stdout.
I'm redirecting stderr to another tty so i'm using eprintln!.
I'm using ropey::Rope as buffer

line and self.y_offset are always correct. Checked them several times.
lines in if i < 3 are correct to render.
Incrementing self.y_offset is working as well.
That's the only function that interacts with stdout besides 1 stdout.flush() in the caller function. (I'm not even sure if it is needed there)

height of terminal (`self.size.1') is 41, but actual visible height is 40.

I have 2 questions:

  • Why i can't use for i in 0..self.size.1? If i use it, lines below will be copied on the line above with each rerender even if i don't change self.y_offset. So after 40 rerenders the bottom line will overlap the first one. That also means i cant use 40th line to render buffer there.
  • Why if i use for i in 0..self.size.1 - 1 it will draw correct while i don't change self.y_offset, and when i change it will copy all lines to line aboves, but instead of doing so every rerender it actually only make it once.

My guess was i didn't overwrite previous buffer and those lines were overwriting new ones if new ones had less length, but that's not the case because i create padding equal to terminal width. And rendering each line in a loop means new line will overwrite the padding if it was too big.

3 posts - 2 participants

Read full topic

🏷️ rust_feed