C14.13: a search hit puts the caret on the match, not the line start

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-12 09:33:02 -04:00
1 parent 584e08d8ad
commit 10b9e7b694
8 files changed
+121 -24

No files matched your search

+27 -3
View File
@@ -49,9 +49,24 @@ pub struct Hit {
pub file: PathBuf,
/// Counted from one.
pub line: usize,
/// The first match, in characters from zero.
pub column: usize,
pub text: String,
}
/// Where lowercase `query` starts in `line`, in characters.
fn column_of(line: &str, query: &str) -> Option<usize> {
// Each lowered byte keeps its character's place.
let (mut lowered, mut places) = (String::new(), Vec::new());
for (place, ch) in line.chars().enumerate() {
for low in ch.to_lowercase() {
lowered.push(low);
places.extend(std::iter::repeat_n(place, low.len_utf8()));
}
}
lowered.find(query).map(|at| places[at])
}
/// Lines of `text` holding `query`, any case.
pub fn lines_with(file: &Path, text: &str, query: &str, hits: &mut Vec<Hit>) {
let query = query.to_lowercase();
@@ -59,11 +74,12 @@ pub fn lines_with(file: &Path, text: &str, query: &str, hits: &mut Vec<Hit>) {
if hits.len() == MOST_HITS {
return;
}
if line.to_lowercase().contains(&query) {
if let Some(column) = column_of(line, &query) {
let shown: String = line.trim().chars().take(160).collect();
hits.push(Hit {
file: file.to_path_buf(),
line: at + 1,
column,
text: shown,
});
}
@@ -108,8 +124,16 @@ mod tests {
);
let lines: Vec<_> = hits
.iter()
.map(|hit| (hit.line, hit.text.as_str()))
.map(|hit| (hit.line, hit.column, hit.text.as_str()))
.collect();
assert_eq!(lines, [(2, "some Words here"), (4, "WORDS")]);
assert_eq!(lines, [(2, 5, "some Words here"), (4, 0, "WORDS")]);
}
#[test]
fn the_column_counts_characters_through_case_changes() {
// İ lowers to two characters.
assert_eq!(super::column_of("İİ née Word", "word"), Some(7));
assert_eq!(super::column_of("中文 搜索", "搜索"), Some(3));
assert_eq!(super::column_of("none", "word"), None);
}
}