use crate::tui_internal::history_cell::HistoryRenderMode;
use crate::tui_internal::history_cell::raw_lines_from_source;
use crate::tui_internal::inline_visualization::DIRECTIVE_PREFIX;
use crate::tui_internal::inline_visualization::InlineVisualizationContext;
use crate::tui_internal::markdown::render_markdown_agent_with_links_cwd_and_visualizations;
use crate::tui_internal::markdown::render_streaming_markdown_agent_with_links_and_cwd;
use crate::tui_internal::terminal_hyperlinks::HyperlinkLine;
use crate::tui_internal::terminal_hyperlinks::plain_hyperlink_lines;
use ratatui::text::Line;
use std::path::Path;
pub(super) struct StreamingRender {
pub(super) lines: Vec<HyperlinkLine>,
stable_source_len: usize,
stable_rendered_len: usize,
has_reference_link_definition: bool,
}
impl StreamingRender {
pub(super) fn new() -> Self {
Self {
lines: Vec::with_capacity(64),
stable_source_len: 0,
stable_rendered_len: 0,
has_reference_link_definition: false,
}
}
pub(super) fn clear(&mut self) {
self.lines.clear();
self.stable_source_len = 0;
self.stable_rendered_len = 0;
self.has_reference_link_definition = false;
}
pub(super) fn recompute(
&mut self,
source: &str,
width: Option<usize>,
cwd: &Path,
render_mode: HistoryRenderMode,
inline_visualization_context: Option<&InlineVisualizationContext>,
) {
self.lines = match (render_mode, inline_visualization_context) {
(HistoryRenderMode::Rich, None) if !source.contains(DIRECTIVE_PREFIX) => {
let rendered =
render_streaming_markdown_agent_with_links_and_cwd(source, width, Some(cwd));
self.has_reference_link_definition = rendered.has_reference_link_definition;
rendered.lines
}
_ => {
self.has_reference_link_definition = false;
render_source(
source,
width,
cwd,
render_mode,
inline_visualization_context,
)
}
};
self.stable_source_len = 0;
self.stable_rendered_len = 0;
}
pub(super) fn append(
&mut self,
raw_source: &str,
committed_source: &str,
width: Option<usize>,
cwd: &Path,
render_mode: HistoryRenderMode,
inline_visualization_context: Option<&InlineVisualizationContext>,
) {
if render_mode == HistoryRenderMode::Raw {
self.lines
.extend(plain_hyperlink_lines(raw_lines_from_source(
committed_source,
)));
return;
}
if raw_source.contains(DIRECTIVE_PREFIX) {
self.recompute(
raw_source,
width,
cwd,
render_mode,
inline_visualization_context,
);
return;
}
if self.has_reference_link_definition {
self.recompute(
raw_source,
width,
cwd,
render_mode,
inline_visualization_context,
);
return;
}
let pending_source = &raw_source[self.stable_source_len..];
let pending =
render_streaming_markdown_agent_with_links_and_cwd(pending_source, width, Some(cwd));
if pending.has_reference_link_definition {
self.has_reference_link_definition = true;
self.recompute(
raw_source,
width,
cwd,
render_mode,
inline_visualization_context,
);
return;
}
let mut newly_stable_rendered_len = None;
if let Some(boundary) = pending.last_top_level_block_start {
let newly_stable_source = &pending_source[..boundary];
let newly_stable = render_source(
newly_stable_source,
width,
cwd,
render_mode,
inline_visualization_context,
);
self.stable_source_len += boundary;
newly_stable_rendered_len = Some(newly_stable.len());
}
self.lines.truncate(self.stable_rendered_len);
if !self.lines.is_empty()
&& (!pending.lines.is_empty() || !pending_source.trim().is_empty())
&& !pending.first_top_level_block_is_html
{
self.lines.push(HyperlinkLine::new(Line::default()));
}
let pending_render_start = self.lines.len();
self.lines.extend(pending.lines);
if let Some(newly_stable_rendered_len) = newly_stable_rendered_len {
self.stable_rendered_len = pending_render_start + newly_stable_rendered_len;
}
}
}
pub(super) fn render_source(
source: &str,
width: Option<usize>,
cwd: &Path,
render_mode: HistoryRenderMode,
inline_visualization_context: Option<&InlineVisualizationContext>,
) -> Vec<HyperlinkLine> {
match render_mode {
HistoryRenderMode::Rich => render_markdown_agent_with_links_cwd_and_visualizations(
source,
width,
Some(cwd),
inline_visualization_context,
),
HistoryRenderMode::Raw => plain_hyperlink_lines(raw_lines_from_source(source)),
}
}
#[cfg(test)]
#[path = "render_tests.rs"]
mod tests;