Skip to main content

rl_lang/repl/
output_render.rs

1//! Converts the [`OutputLine`] buffer into ratatui [`Line`]s for rendering.
2//!
3//! `ValidInput` lines are intentionally filtered out - they exist only for
4//! `:save` and are never shown in the output area.
5//!
6//! `Result` lines run through [`highlight`] first; if the highlighter returns
7//! only red or white spans (i.e. plain text, not code), the line is rendered
8//! as plain green instead.
9
10use ratatui::{
11    style::{Color, Modifier, Style},
12    text::{Line, Span},
13};
14
15use crate::repl::{lines_types::OutputLine, syntax_highlighting::highlight};
16
17/// Converts the output buffer into a list of styled ratatui lines ready to render.
18pub fn render_output(output: &[OutputLine]) -> Vec<Line<'static>> {
19    output
20        .iter()
21        .filter_map(|line| match line {
22            OutputLine::Input(s) => {
23                // strips ".. "
24                let (prefix, code) = if let Some(stripped) = s.strip_prefix(".. ") {
25                    (".. ", stripped)
26                } else {
27                    (">> ", s.as_str())
28                };
29                let mut spans = vec![Span::styled(
30                    prefix,
31                    Style::default()
32                        .fg(Color::Cyan)
33                        .add_modifier(Modifier::BOLD),
34                )];
35                spans.extend(highlight(code));
36                Some(Line::from(spans))
37            }
38            OutputLine::ValidInput(_) => None,
39            OutputLine::Result(s) => {
40                // try to highlight if it is correct
41                let spans = highlight(s);
42                // highlight returns red on lex failure
43                let is_code = spans.iter().any(|sp| {
44                    sp.style
45                        .fg
46                        .map(|c| c != Color::Red && c != Color::White)
47                        .unwrap_or(false)
48                });
49                if is_code {
50                    Some(Line::from(spans))
51                } else {
52                    Some(Line::from(Span::styled(
53                        s.clone(),
54                        Style::default().fg(Color::Green),
55                    )))
56                }
57            }
58            OutputLine::Error(s) => Some(Line::from(vec![
59                Span::styled(
60                    "✗ ",
61                    Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
62                ),
63                Span::styled(s.clone(), Style::default().fg(Color::Red)),
64            ])),
65            OutputLine::Info(s) => Some(Line::from(Span::styled(
66                s.clone(),
67                Style::default().fg(Color::DarkGray),
68            ))),
69            OutputLine::Separator => Some(Line::from(Span::styled(
70                "─".repeat(40),
71                Style::default().fg(Color::DarkGray),
72            ))),
73            OutputLine::Styled(parts) => Some(Line::from(
74                parts
75                    .iter()
76                    .map(|(text, style)| Span::styled(text.clone(), *style))
77                    .collect::<Vec<_>>(),
78            )),
79        })
80        .collect()
81}