mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Preserve user prompt URL destinations across terminal widths (#47360)
## Why User prompt URLs only received explicit hyperlink destinations when they required forced wrapping, leaving other URLs without those annotations. ## What changed Annotate web URLs before wrapping user messages so their full destinations survive both adaptive wrapping and forced splitting of oversized URLs. ## Testing Add a regression test and snapshot at widths of 120, 60, and 24 columns, checking URL destinations, line width limits, and matching display and transcript output, including a prompt with an image placeholder and a parenthesized URL. GitOrigin-RevId: fccae7b76a3613776d7a663c41e699ebad7eebc1
This commit is contained in:
@@ -5,7 +5,7 @@ use super::markdown_render_cache::MarkdownRenderCache;
|
||||
use super::*;
|
||||
use crate::style::accent_color_on;
|
||||
use crate::style::history_prompt_style;
|
||||
use crate::terminal_hyperlinks::annotate_web_urls_in_line;
|
||||
use crate::terminal_hyperlinks::annotate_web_urls;
|
||||
use crate::terminal_hyperlinks::lines_with_sources_eq;
|
||||
use crate::terminal_hyperlinks::remap_source_wrapped_line;
|
||||
use crate::wrapping::url_preserving_wrap_options;
|
||||
@@ -289,18 +289,17 @@ fn wrap_user_message(
|
||||
build_user_message_lines_with_elements(message, text_elements, style, element_style)
|
||||
};
|
||||
let mut wrapped = crate::terminal_hyperlinks::adaptive_wrap_hyperlink_lines(
|
||||
&plain_hyperlink_lines(logical_lines),
|
||||
&annotate_web_urls(logical_lines),
|
||||
wrap_options,
|
||||
)
|
||||
.into_iter()
|
||||
.flat_map(|mut line| {
|
||||
.flat_map(|line| {
|
||||
if line.width() <= usize::from(wrap_width) {
|
||||
return vec![line];
|
||||
}
|
||||
|
||||
// Terminal autowrap loses the message gutter and background. Explicitly split
|
||||
// oversized URL tokens while retaining their complete OSC-8 destination.
|
||||
line.hyperlinks = annotate_web_urls_in_line(line.line.clone()).hyperlinks;
|
||||
let forced_lines = word_wrap_line_with_source(
|
||||
&line.line,
|
||||
url_preserving_wrap_options(RtOptions::new(usize::from(wrap_width)))
|
||||
|
||||
@@ -3,6 +3,55 @@ use crate::history_cell::markdown_render_cache::MarkdownRenderCacheKey;
|
||||
use assert_matches::assert_matches;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn user_prompt_urls_keep_destinations_across_widths() {
|
||||
let url = "https://example.test/terminal-link-wrapping/solid-octants";
|
||||
let placeholder = "[Image #1]";
|
||||
let message = format!(
|
||||
"from this site, write out the ascii for solid octants 18 columns {url}\n{placeholder} See (https://example.test/a)."
|
||||
);
|
||||
let image_start = message.find(placeholder).unwrap();
|
||||
let cell = new_user_prompt(
|
||||
message,
|
||||
vec![TextElement::new(
|
||||
(image_start..image_start + placeholder.len()).into(),
|
||||
Some(placeholder.to_string()),
|
||||
)],
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
);
|
||||
let mut snapshots = Vec::new();
|
||||
for width in [120, 60, 24] {
|
||||
let lines = cell.display_hyperlink_lines(width);
|
||||
assert_eq!(cell.transcript_hyperlink_lines(width), lines);
|
||||
assert!(lines.iter().all(|line| line.width() <= usize::from(width)));
|
||||
for destination in [url, "https://example.test/a"] {
|
||||
let linked_text = lines
|
||||
.iter()
|
||||
.flat_map(|line| {
|
||||
line.hyperlinks
|
||||
.iter()
|
||||
.filter(|link| link.destination == destination)
|
||||
.flat_map(|link| {
|
||||
line.line
|
||||
.to_string()
|
||||
.chars()
|
||||
.skip(link.columns.start)
|
||||
.take(link.columns.len())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
})
|
||||
.collect::<String>();
|
||||
assert_eq!(linked_text, destination, "width {width}");
|
||||
}
|
||||
snapshots.push(format!(
|
||||
"width {width}\n{}",
|
||||
ratatui::text::Text::from(visible_lines(lines))
|
||||
));
|
||||
}
|
||||
insta::assert_snapshot!(snapshots.join("\n\n"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_image_only_user_message_remains_visible() {
|
||||
let cell = new_user_prompt(
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
---
|
||||
source: tui/src/history_cell/messages_tests.rs
|
||||
expression: "snapshots.join(\"\\n\\n\")"
|
||||
---
|
||||
width 120
|
||||
|
||||
› from this site, write out the ascii for solid octants 18 columns
|
||||
https://example.test/terminal-link-wrapping/solid-octants
|
||||
[Image #1] See (https://example.test/a).
|
||||
|
||||
|
||||
width 60
|
||||
|
||||
› from this site, write out the ascii for solid octants 18
|
||||
columns
|
||||
https://example.test/terminal-link-wrapping/solid-octants
|
||||
[Image #1] See (https://example.test/a).
|
||||
|
||||
|
||||
width 24
|
||||
|
||||
› from this site, write
|
||||
out the ascii for
|
||||
solid octants 18
|
||||
columns
|
||||
https://example.test/
|
||||
terminal-link-wrappin
|
||||
g/solid-octants
|
||||
[Image #1] See
|
||||
(https://example.test
|
||||
/a).
|
||||
Reference in New Issue
Block a user