mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Render async question prompts as Markdown in the TUI (#55411)
## What changed Render async question titles with the Markdown renderer so emphasis, fenced code blocks, and labeled links display with their intended formatting. Remove blanket bold styling and wrap overwide tokens while preserving hyperlink destinations and accurate row counts. ## Testing Update rendering snapshots and extend the question rendering test to cover Markdown formatting, link destinations, selective bold styling, and equivalent LF and CRLF rendering at narrow and wide widths. GitOrigin-RevId: b3e8a94a04433c2d9e5d11e66f83cbd16959d8ed
This commit is contained in:
committed by
copyberry
parent
7827cd886d
commit
1a35bc4ccb
@@ -17,10 +17,6 @@ use crate::keymap::KeymapContext;
|
||||
use crate::keymap::ListAction;
|
||||
use crate::keymap::RuntimeKeymap;
|
||||
use crate::terminal_hyperlinks::HyperlinkLine;
|
||||
use crate::terminal_hyperlinks::annotate_web_urls_in_line;
|
||||
use crate::terminal_hyperlinks::remap_source_wrapped_line;
|
||||
use crate::wrapping::WrappedLine;
|
||||
use crate::wrapping::wrap_ranges_trim;
|
||||
use codex_protocol::items::AsyncUserInputQuestion;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
@@ -156,26 +152,29 @@ impl AsyncQuestions {
|
||||
}
|
||||
|
||||
pub(super) fn wrapped_question_lines(&self, width: u16) -> Vec<HyperlinkLine> {
|
||||
let width = usize::from(width.max(1));
|
||||
self.current_question()
|
||||
.map(|q| {
|
||||
// Line::from removes line endings, so wrap and annotate each logical line
|
||||
// separately to keep source byte ranges aligned with the visible text.
|
||||
q.title
|
||||
.split('\n')
|
||||
.flat_map(|text| {
|
||||
let text = text.strip_suffix('\r').unwrap_or(text);
|
||||
let source = annotate_web_urls_in_line(text.to_owned().into());
|
||||
let wrapped = wrap_ranges_trim(text, usize::from(width.max(1)))
|
||||
.into_iter()
|
||||
.map(|range| WrappedLine {
|
||||
line: (&text[range.clone()]).into(),
|
||||
range,
|
||||
prefix_bytes: 0,
|
||||
})
|
||||
.collect();
|
||||
remap_source_wrapped_line(&source, wrapped)
|
||||
})
|
||||
.collect()
|
||||
.map(|question| {
|
||||
crate::markdown_render::render_markdown_lines_with_width_cwd_and_hidden_link_destinations(
|
||||
&question.title,
|
||||
Some(width),
|
||||
/*cwd*/ None,
|
||||
&crate::markdown_render::hide_web_link_destination,
|
||||
)
|
||||
.into_iter()
|
||||
.flat_map(|line| {
|
||||
if line.width() <= width {
|
||||
vec![line]
|
||||
} else {
|
||||
// The question layout counts physical rows, so break overwide Markdown
|
||||
// tokens while preserving their hyperlink destinations.
|
||||
crate::terminal_hyperlinks::remap_source_wrapped_line(
|
||||
&line,
|
||||
crate::wrapping::word_wrap_line_with_source(&line.line, width),
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ impl Renderable for AsyncQuestions {
|
||||
Paragraph::new(self.progress_prefix_text().dim()).render(sections.progress_area, buf);
|
||||
HyperlinkParagraph::new(
|
||||
§ions.question_lines,
|
||||
crate::style::accent_style().bold(),
|
||||
crate::style::accent_style().not_bold(),
|
||||
)
|
||||
.render(sections.question_area, buf);
|
||||
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/async_questions/state_tests.rs
|
||||
assertion_line: 483
|
||||
expression: buffer_text(&clipped)
|
||||
---
|
||||
|
||||
Long question Long question Long question
|
||||
Long question Long question Long question Long
|
||||
|
||||
+3
-3
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/async_questions/state_tests.rs
|
||||
assertion_line: 220
|
||||
expression: "rendered[0]"
|
||||
---
|
||||
|
||||
@@ -7,11 +8,10 @@ expression: "rendered[0]"
|
||||
|
||||
Run this command:
|
||||
|
||||
```sh
|
||||
printf '診 断 '
|
||||
```
|
||||
Then review (https://example.com/diagnostics?view=full)?
|
||||
|
||||
Then review the diagnostics
|
||||
(https://example.com/diagnostics?view=full)?
|
||||
|
||||
Type your answer
|
||||
|
||||
|
||||
+4
-4
@@ -1,13 +1,13 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/async_questions/state_tests.rs
|
||||
assertion_line: 172
|
||||
assertion_line: 171
|
||||
expression: "crate::terminal_hyperlinks::strip_osc8(&buffer_text(&buffer))"
|
||||
---
|
||||
|
||||
2 of 2
|
||||
Review this change (https://
|
||||
github.com/openai/codex/pull/12345?
|
||||
diff=split)?
|
||||
Review this change
|
||||
(https://github.com/openai/codex/
|
||||
pull/12345?diff=split)?
|
||||
|
||||
› 1. Named
|
||||
2. Other
|
||||
|
||||
+8
-7
@@ -1,13 +1,14 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/async_questions/state_tests.rs
|
||||
assertion_line: 253
|
||||
expression: buffer_text(&buffer)
|
||||
---
|
||||
|
||||
Long question Long question Long question
|
||||
Long question Long question Long question
|
||||
Long question Long question Long question
|
||||
Long question Long question Long question
|
||||
Long question Long question Long question
|
||||
Long question Long question Long question
|
||||
Long question Long question Long question
|
||||
Long question Long question Long question Long
|
||||
question Long question Long question Long
|
||||
question Long question Long question Long
|
||||
question Long question Long question Long
|
||||
question Long question Long question Long
|
||||
question Long question Long question Long
|
||||
question Long question Long question Long
|
||||
› 2. Other
|
||||
|
||||
@@ -181,9 +181,11 @@ fn wrapped_question_url_keeps_its_complete_destination() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiline_question_preserves_line_breaks_and_link_destinations() {
|
||||
fn markdown_question_preserves_formatting_and_link_destinations() {
|
||||
let url = "https://example.com/diagnostics?view=full";
|
||||
let title = format!("Run this command:\n\n```sh\nprintf '診断'\n```\nThen review ({url})?\n");
|
||||
let title = format!(
|
||||
"Run **this command**:\n\n```sh\nprintf '診断'\n```\nThen review the [diagnostics]({url})?\n"
|
||||
);
|
||||
let mut rendered = Vec::new();
|
||||
for line_ending in ["\n", "\r\n"] {
|
||||
let mut editor = editor();
|
||||
@@ -207,9 +209,17 @@ fn multiline_question_preserves_line_breaks_and_link_destinations() {
|
||||
})
|
||||
})
|
||||
.collect::<String>();
|
||||
assert_eq!(linked_text, url);
|
||||
assert!(linked_text.contains("diagnostics"));
|
||||
let text = crate::terminal_hyperlinks::strip_osc8(&buffer_text(&buffer));
|
||||
if width == 60 {
|
||||
let bold_text = buffer
|
||||
.content
|
||||
.iter()
|
||||
.filter(|cell| cell.modifier.contains(Modifier::BOLD))
|
||||
.map(ratatui::buffer::Cell::symbol)
|
||||
.collect::<String>();
|
||||
assert!(bold_text.contains("this command"));
|
||||
assert!(!bold_text.contains("Run "));
|
||||
rendered.push(text);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user