Skip to content

Commit

Permalink
fix(ui): properly handle escape codes in persisted output (#7760)
Browse files Browse the repository at this point in the history
### Description

Correctly persist bytes sent to the app. 

This PR has 3 fixes:
 - Support non-utf8 logs
 - Support multiline writes
 - Support ANSI escape sequences

All these are achieved by putting the bytes through a `vt100` parser and
rendering the resulting screen.

Future work might be to avoid emitting the ansi escape codes and
construct a `Text` widget directly, but this is a larger lift as we need
to stop using `console::Style` throughout the codebase.

### Testing Instructions

Added unit test

Did a quick manual test of `PersistedWriter` to make sure writes get
persisted when they should in `turbo`


Closes TURBO-2655
  • Loading branch information
chris-olszewski authored Mar 19, 2024
1 parent 0f31686 commit 0fc0fc2
Showing 1 changed file with 43 additions and 5 deletions.
48 changes: 43 additions & 5 deletions crates/turborepo-ui/src/tui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use std::{
use ratatui::{
backend::{Backend, CrosstermBackend},
layout::{Constraint, Layout},
text::Text,
widgets::Widget,
Frame, Terminal,
};
use tui_term::widget::PseudoTerminal;

const HEIGHT: u16 = 60;
const PANE_HEIGHT: u16 = 40;
Expand Down Expand Up @@ -100,10 +100,7 @@ fn run_app_inner<B: Backend>(

while let Some(event) = poll(app.interact, &receiver, last_render + FRAMERATE) {
if let Some(message) = update(&mut app, event)? {
// TODO: use term emulator to properly render this, blocked by PR #7713
terminal.insert_before(1, |buf| {
Text::raw(String::from_utf8_lossy(&message)).render(buf.area, buf)
})?;
persist_bytes(terminal, &message)?;
}
if app.done {
break;
Expand Down Expand Up @@ -202,3 +199,44 @@ fn view<I>(app: &mut App<I>, f: &mut Frame) {
app.table.stateful_render(f, table);
f.render_widget(&app.pane, pane);
}

/// Write provided bytes to a section of the screen that won't get rewritten
fn persist_bytes(terminal: &mut Terminal<impl Backend>, bytes: &[u8]) -> Result<(), Error> {
let size = terminal.size()?;
let mut parser = turborepo_vt100::Parser::new(size.height, size.width, 128);
parser.process(bytes);
let screen = parser.entire_screen();
let (height, _) = screen.size();
terminal.insert_before(height as u16, |buf| {
PseudoTerminal::new(&screen).render(buf.area, buf)
})?;
Ok(())
}

#[cfg(test)]
mod test {
use ratatui::{backend::TestBackend, buffer::Buffer};

use super::*;

#[test]
fn test_persist_bytes() {
let mut term = Terminal::with_options(
TestBackend::new(10, 7),
ratatui::TerminalOptions {
viewport: ratatui::Viewport::Inline(3),
},
)
.unwrap();
persist_bytes(&mut term, b"two\r\nlines").unwrap();
term.backend().assert_buffer(&Buffer::with_lines(vec![
"two ",
"lines ",
" ",
" ",
" ",
" ",
" ",
]));
}
}

0 comments on commit 0fc0fc2

Please sign in to comment.