project: Normalize line endings in textual project search (#58628)

Release Notes:

- Fixed an issue with project search on windows where multiline string
searches would not work correctly due to newline differences
This commit is contained in:
Lukas Wirth authored and GitHub committed 2026-06-05 10:44:29 +00:00
1 parent 2d18262735
commit 6ca46fcf2c
2 files changed
+74 -2

No files matched your search

+6 -2
View File
@@ -103,7 +103,8 @@ impl SearchQuery {
match_full_paths: bool,
buffers: Option<Vec<Entity<Buffer>>>,
) -> Result<Self> {
let query = query.to_string();
let mut query = query.to_string();
text::LineEnding::normalize(&mut query);
if !case_sensitive && !query.is_ascii() {
// AhoCorasickBuilder doesn't support case-insensitive search with unicode characters
// Fallback to regex search as recommended by
@@ -189,7 +190,8 @@ impl SearchQuery {
match_full_paths: bool,
buffers: Option<Vec<Entity<Buffer>>>,
) -> Result<Self> {
let query = query.to_string();
let mut query = query.to_string();
text::LineEnding::normalize(&mut query);
let inner = SearchInputs {
query: Arc::from(query.as_str()),
files_to_include,
@@ -402,6 +404,7 @@ impl SearchQuery {
let mut text = String::new();
if query_str.contains('\n') {
reader.read_to_string(&mut text)?;
text::LineEnding::normalize(&mut text);
Ok(search.is_match(&text))
} else {
let mut bytes_read = 0;
@@ -425,6 +428,7 @@ impl SearchQuery {
let mut text = String::new();
if *multiline {
reader.read_to_string(&mut text)?;
text::LineEnding::normalize(&mut text);
Ok(regex.is_match(&text)?)
} else {
let mut bytes_read = 0;
@@ -7443,6 +7443,74 @@ async fn test_search(cx: &mut gpui::TestAppContext) {
);
}
#[gpui::test]
async fn test_search_multiline_crlf(cx: &mut gpui::TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
path!("/dir"),
json!({
"crlf.rs": "alpha\r\nbeta\r\ngamma",
"lf.rs": "alpha\nbeta\ngamma",
}),
)
.await;
let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
// A query with CRLF line endings should match buffers regardless of their
// on-disk line endings (buffers always store normalized `\n`).
assert_eq!(
search(
&project,
SearchQuery::text(
"alpha\r\nbeta",
false,
true,
false,
Default::default(),
Default::default(),
false,
None,
)
.unwrap(),
cx
)
.await
.unwrap(),
HashMap::from_iter([
(path!("dir/crlf.rs").to_string(), vec![0..10]),
(path!("dir/lf.rs").to_string(), vec![0..10]),
])
);
// The reverse: a query with LF line endings should match a file that is
// stored with CRLF line endings on disk.
assert_eq!(
search(
&project,
SearchQuery::text(
"alpha\nbeta",
false,
true,
false,
Default::default(),
Default::default(),
false,
None,
)
.unwrap(),
cx
)
.await
.unwrap(),
HashMap::from_iter([
(path!("dir/crlf.rs").to_string(), vec![0..10]),
(path!("dir/lf.rs").to_string(), vec![0..10]),
])
);
}
#[gpui::test]
async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
init_test(cx);