Skip to content

Commit 554af75

Browse files
committed
test(cli-rs): a theme switch builds no fence or diff again
A work counter for P11: the widgets one switch creates are as many with 0, 3 or 36 fences and with 1 or 12 edit diffs. Remounting the route or a renderer that takes a fence on restyle makes it fail. The restyle comment no longer says a table option change rebuilds every code block, which OpenTUI #2105 changed.
1 parent af84f75 commit 554af75

2 files changed

Lines changed: 58 additions & 2 deletions

File tree

‎packages/cli-rs/crates/tui/tests/all/transcript.rs‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use opencode_api::generated::V2Event;
1313
use opencode_api::transport::{BoxFuture, Request, Response, Transport};
1414
use opencode_local::cli_config::{Bounded, Debug, Info, Markdown, Scroll, Session, TurnTokens, Verbosity, Visibility};
1515
use opencode_tui::Deps;
16+
use opentui::widget::Widget;
1617
use serde_json::{Value, json};
1718
use tokio::sync::Semaphore;
1819

@@ -507,6 +508,61 @@ fn a_theme_switch_restyles_the_transcript_in_place() {
507508
assert_eq!(diff.map(|(x, y)| (x, y, switched.cell(x, y), expected.cell(x, y))), None, "{}", switched.text());
508509
}
509510

511+
/// P11 as a work counter: a theme switch builds no fence or diff again, so it parses none. The widgets one switch
512+
/// creates are as many with 0, 3 or 36 fences, and with 1 or 12 edit diffs. A kept code view restyles its highlight
513+
/// without a parse (OpenTUI's `option_changes_keep_builtin_fences_streaming` counts them).
514+
#[test]
515+
fn a_theme_switch_builds_no_fence_or_diff_again() {
516+
let fence = |language: &str, line: &str| format!("```{language}\n{}```\n", format!("{line}\n").repeat(15));
517+
let reply = |fences: bool| {
518+
let code = [fence("rust", "let x = parse(\"a\", 1); // b"), fence("python", "total += f(1) # n")];
519+
if fences { format!("Reply.\n\n{}\n{}\n{}", code[0], code[1], code[0]) } else { "Reply.".to_owned() }
520+
};
521+
let edit = |index: usize| {
522+
let lines: String =
523+
(0..15).map(|line| format!("- let a{line} = {line};\n+ let b{line} = {line};\n")).collect();
524+
json!([{
525+
"type": "tool", "id": format!("call_{index}"), "name": "edit", "time": { "created": 1, "completed": 2 },
526+
"state": {
527+
"status": "completed", "input": { "path": format!("/project/src/m{index}.rs") },
528+
"content": [{ "type": "text", "text": "Edited" }],
529+
"metadata": { "files": [{
530+
"file": format!("src/m{index}.rs"), "filePath": format!("/project/src/m{index}.rs"), "type": "update",
531+
"patch": format!("--- src/m{index}.rs\n+++ src/m{index}.rs\n@@ -1,16 +1,16 @@\n fn main() {{\n{lines}"),
532+
"additions": 15, "deletions": 15,
533+
}] },
534+
},
535+
}])
536+
};
537+
let created = |replies: usize, content: &dyn Fn(usize) -> Value| {
538+
let messages = (0..replies as u64)
539+
.flat_map(|index| {
540+
let id = |suffix: &str| format!("msg_{index:03}{suffix}");
541+
let at = 1000 + index * 10_000;
542+
[user(&id("a"), at, "Go"), assistant(&id("b"), at + 100, content(index as usize), Some(at + 5000))]
543+
})
544+
.collect();
545+
let h = open(120, 200, "ses_w", messages, support::still());
546+
h.wait_for("Reply.");
547+
h.app.render();
548+
let first = opentui::widget::View::new(&h.app).id();
549+
assert!(h.tui.theme.borrow_mut().set("tokyonight"));
550+
h.tui.theme_changed();
551+
h.app.render();
552+
opentui::widget::View::new(&h.app).id() - first
553+
};
554+
let text = |fences: bool| move |_: usize| json!([{ "type": "text", "text": reply(fences) }]);
555+
let with_edit = |index: usize| {
556+
let mut content = edit(index);
557+
content.as_array_mut().unwrap().insert(0, json!({ "type": "text", "text": "Reply." }));
558+
content
559+
};
560+
let counts = [created(12, &text(false)), created(1, &text(true)), created(12, &text(true))];
561+
assert_eq!(counts, [counts[0]; 3], "widgets per switch at 0, 3 and 36 fences");
562+
let counts = [created(1, &with_edit), created(12, &with_edit)];
563+
assert_eq!(counts, [counts[0]; 2], "widgets per switch at 1 and 12 edit diffs");
564+
}
565+
510566
/// `session.markdown` reaches the text already shown, as TS's `conceal` prop follows `markdownMode()`
511567
/// (`message-parts.tsx:195`), and the fence keeps its widget.
512568
#[test]

‎packages/cli-rs/crates/ui/src/markdown.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ pub fn view_colors(theme: &Theme, syntax: &SyntaxTheme) -> MarkdownColors {
8787
/// stay, and code keeps its highlight, so nothing is parsed again. Plugin fences render again and read the theme
8888
/// themselves.
8989
///
90-
/// OpenTUI builds every code block again when the table options change. They follow the `conceal` scope, which
91-
/// [`crate::code::syntax_theme`] leaves out, so the border stays gray and the options stay the same.
90+
/// The table options follow the `conceal` scope, which [`crate::code::syntax_theme`] leaves out, so they stay the
91+
/// same. A new border color would repaint the tables in place.
9292
pub fn restyle(markdown: &Markdown, colors: MarkdownColors, syntax: &SyntaxTheme) {
9393
markdown.colors(colors).syntax_theme(syntax.clone()).table_options(table(syntax));
9494
}

0 commit comments

Comments
 (0)