diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-08 00:44:49 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-08 00:44:49 +0300 |
| commit | d6285aaa59e0f7908c2ece81314aff2b98847e31 (patch) | |
| tree | ec1306441ebb87592ff120e2bd34c55028f1fd33 /src/app | |
| parent | 35e9838a44470c418ae51204bab54115d15d87bd (diff) | |
fix: avoid render-time plain url scans
Diffstat (limited to 'src/app')
| -rw-r--r-- | src/app/actions.rs | 14 | ||||
| -rw-r--r-- | src/app/input/terminal.rs | 96 |
2 files changed, 37 insertions, 73 deletions
diff --git a/src/app/actions.rs b/src/app/actions.rs index 12c0df87..5c263e7e 100644 --- a/src/app/actions.rs +++ b/src/app/actions.rs @@ -2122,20 +2122,6 @@ pub(crate) fn url_at_column(row: &str, col: u16) -> Option<&str> { safe_web_url(row.get(start_byte..end_byte)?) } -pub(crate) fn visible_url_spans(row: &str) -> Vec<(u16, u16, &str)> { - let cells = text_cells(row); - url_spans(&cells) - .into_iter() - .filter_map(|span| { - let start_byte = byte_index_for_cell(row, span.start); - let end_byte = byte_index_after_cell(row, span.end); - let url = row.get(start_byte..end_byte).and_then(safe_web_url)?; - let (start_col, end_col) = span.columns(&cells); - Some((start_col, end_col, url)) - }) - .collect() -} - fn url_spans(cells: &[TextCell]) -> Vec<CellSpan> { let mut spans = Vec::new(); let mut start = 0; diff --git a/src/app/input/terminal.rs b/src/app/input/terminal.rs index c9abbbc2..a8e364ba 100644 --- a/src/app/input/terminal.rs +++ b/src/app/input/terminal.rs @@ -616,92 +616,70 @@ mod tests { } #[tokio::test] - async fn render_stream_synthesizes_soft_wrapped_url_hyperlinks() { + async fn render_stream_does_not_synthesize_soft_wrapped_url_hyperlinks() { let (_app, info) = app_with_screen_bytes(b""); let prefix = "https://example.com/"; let padding = "b".repeat(info.inner_rect.width as usize - prefix.len()); let url = format!("{prefix}{padding}tail"); - let (app, info) = app_with_screen_bytes(url.as_bytes()); + let (app, _info) = app_with_screen_bytes(url.as_bytes()); - let links = crate::server::render_stream::visible_hyperlinks( - &app.state, - &app.terminal_runtimes, - None, - ); + let links = + crate::server::render_stream::visible_hyperlinks(&app.state, &app.terminal_runtimes); - assert!(links.iter().any(|((x, y), symbol, uri)| { - *x == info.inner_rect.x + 1 - && *y == info.inner_rect.y + 1 - && symbol == "a" - && uri == &url - })); + assert!(links.is_empty()); } #[tokio::test] - async fn render_stream_does_not_shift_url_hyperlinks_after_zero_width_mark() { + async fn render_stream_does_not_synthesize_url_hyperlinks_after_zero_width_mark() { let url = "https://example.com/mark"; let screen = format!("e\u{301} {url}"); - let (app, info) = app_with_screen_bytes(screen.as_bytes()); + let (app, _info) = app_with_screen_bytes(screen.as_bytes()); - let links = crate::server::render_stream::visible_hyperlinks( - &app.state, - &app.terminal_runtimes, - None, - ); + let links = + crate::server::render_stream::visible_hyperlinks(&app.state, &app.terminal_runtimes); - assert!(links.iter().any(|((x, y), symbol, uri)| { - *x == info.inner_rect.x + 2 && *y == info.inner_rect.y && symbol == "h" && uri == url - })); + assert!(links.is_empty()); } #[tokio::test] - async fn render_stream_handles_hard_newline_after_full_row() { + async fn render_stream_does_not_synthesize_hard_newline_plain_url_hyperlinks() { let (_app, info) = app_with_screen_bytes(b""); let full_row = "x".repeat(info.inner_rect.width as usize); let url = "https://example.com/next"; let screen = format!("{full_row}\n{url}"); - let (app, info) = app_with_screen_bytes(screen.as_bytes()); - let links = crate::server::render_stream::visible_hyperlinks( - &app.state, - &app.terminal_runtimes, - None, - ); + let (app, _info) = app_with_screen_bytes(screen.as_bytes()); + let links = + crate::server::render_stream::visible_hyperlinks(&app.state, &app.terminal_runtimes); - assert!(links.iter().any(|((x, y), symbol, uri)| { - *x == info.inner_rect.x + 1 - && *y == info.inner_rect.y + 2 - && symbol == "t" - && uri == url - })); + assert!(links.is_empty()); } #[tokio::test] - async fn render_stream_filters_plain_url_when_final_cell_differs() { - let line = "see https://example.com/hidden"; - let (mut app, _info) = app_with_screen_bytes(line.as_bytes()); - let (mut buffer, _cursor) = - crate::server::render_stream::render_virtual_with_runtime_registry( - &mut app.state, - &app.terminal_runtimes, - ratatui::layout::Rect::new(0, 0, 106, 20), - false, - crate::kitty_graphics::HostCellSize::default(), - ); - let info = app.state.view.pane_infos[0].clone(); - let target_x = info.inner_rect.x + line.find("https").expect("url start") as u16; - let target_y = info.inner_rect.y; - buffer[(target_x, target_y)] - .set_style(ratatui::style::Style::default().fg(ratatui::style::Color::Red)); - - let links = crate::server::render_stream::visible_hyperlinks( - &app.state, + async fn render_stream_exports_osc8_hyperlink_metadata() { + let uri = "https://example.com/target"; + let (mut app, _info) = + app_with_screen_bytes(format!("\x1b]8;;{uri}\x1b\\label\x1b]8;;\x1b\\").as_bytes()); + let (buffer, cursor) = crate::server::render_stream::render_virtual_with_runtime_registry( + &mut app.state, &app.terminal_runtimes, - Some(&buffer), + ratatui::layout::Rect::new(0, 0, 106, 20), + false, + crate::kitty_graphics::HostCellSize::default(), ); - - assert!(!links + let links = + crate::server::render_stream::visible_hyperlinks(&app.state, &app.terminal_runtimes); + let frame = crate::protocol::FrameData::from_ratatui_buffer_with_hyperlinks( + &buffer, cursor, &links, + ); + let ((x, y), symbol, _) = links .iter() - .any(|((x, y), _, uri)| *x == target_x && *y == target_y && uri == &line[4..])); + .find(|(_, symbol, link_uri)| symbol == "l" && link_uri == uri) + .expect("OSC 8 link cell"); + let linked_cell_index = usize::from(*y) * usize::from(frame.width) + usize::from(*x); + + assert_eq!(frame.hyperlinks, vec![uri.to_owned()]); + assert_eq!(symbol, "l"); + assert_eq!(frame.cells[linked_cell_index].hyperlink, Some(0)); } #[tokio::test] |
