From a6c5f7400a0c82433b8a60bb776b3e38ad50219c Mon Sep 17 00:00:00 2001 From: Daniel Edrisian Date: Wed, 20 Aug 2025 14:26:56 -0700 Subject: [PATCH] win --- codex-rs/tui/src/chatwidget.rs | 30 ++++++++++++++++++++++++++++ codex-rs/tui/src/chatwidget/tests.rs | 28 ++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 7d57d9a8e1..819f70fc0a 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -681,6 +681,7 @@ impl ChatWidget<'_> { } // Find a trailing quoted path or URL token at the end of the current input. + // Also handles Windows-style paths like C:\Users\... without quotes. // Returns (start_idx, end_idx, candidate_str) in byte indices if found. fn extract_trailing_path_candidate(s: &str) -> Option<(usize, usize, String)> { let trimmed_end = s.trim_end(); @@ -724,6 +725,35 @@ impl ChatWidget<'_> { return Some((start_idx, end_idx, token.to_string())); } } + + // Case 3: Heuristic for Windows drive-letter paths without quotes, possibly containing spaces. + // Look for a trailing pattern like :\ or :/ and treat the remainder + // of the line as the candidate path. + // Example: "See C:\\Users\\John Doe\\image 1.png" -> candidate starts at 'C'. + if let Some(colon_pos) = trimmed_end.rfind(':') { + if colon_pos > 0 && colon_pos + 1 < end { + let bytes = trimmed_end.as_bytes(); + let prev = bytes[colon_pos - 1]; + let next = bytes[colon_pos + 1]; + let is_letter = (prev as char).is_ascii_alphabetic(); + let is_sep = next == b'\\' || next == b'/'; + if is_letter && is_sep { + let start_idx = colon_pos - 1; // include the drive letter + let end_idx = end; // take until end of line (may include spaces) + let cand = &trimmed_end[start_idx..end_idx]; + return Some((start_idx, end_idx, cand.to_string())); + } + } + } + + // Case 4: UNC paths. Look for the last occurrence of \\\\ and consider the remainder + // of the line as the path candidate. + if let Some(pos) = trimmed_end.rfind("\\\\") { + if pos + 1 < end && &trimmed_end[pos..pos + 2] == "\\\\" { + let cand = &trimmed_end[pos..end]; + return Some((pos, end, cand.to_string())); + } + } } None diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 192ab3c6ae..0ab1f54287 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -170,6 +170,34 @@ fn lines_to_single_string(lines: &[ratatui::text::Line<'static>]) -> String { s } +#[test] +fn extract_trailing_windows_drive_path_token_end() { + let input = r"Check this C:\\Users\\me\\pic.png".to_string(); + let res = ChatWidget::extract_trailing_path_candidate(&input).expect("candidate"); + let (_, _, cand) = res; + assert!(cand.ends_with("pic.png")); + assert!(cand.starts_with("C:")); +} + +#[test] +fn extract_trailing_windows_drive_path_with_spaces() { + let input = r"Please use C:\\Users\\John Doe\\Pictures\\image 1.png".to_string(); + let res = ChatWidget::extract_trailing_path_candidate(&input).expect("candidate"); + let (_, _, cand) = res; + assert!(cand.starts_with("C:")); + assert!(cand.contains("John Doe")); + assert!(cand.ends_with("image 1.png")); +} + +#[test] +fn extract_trailing_unc_path() { + let input = r"See \\\\server\\share\\img.jpg".to_string(); + let res = ChatWidget::extract_trailing_path_candidate(&input).expect("candidate"); + let (_, _, cand) = res; + assert!(cand.starts_with("\\\\")); + assert!(cand.ends_with("img.jpg")); +} + fn open_fixture(name: &str) -> std::fs::File { // 1) Prefer fixtures within this crate {