Merge remote-tracking branch 'origin/main' into global-gitignore

This commit is contained in:
Cole Miller 2025-07-29 10:43:15 -04:00
commit 17822762b7
1193 changed files with 112606 additions and 40043 deletions

View file

@ -42,7 +42,6 @@ use unindent::Unindent as _;
use util::{
TryFutureExt as _, assert_set_eq, maybe, path,
paths::PathMatcher,
separator,
test::{TempTree, marked_text_offsets},
uri,
};
@ -329,6 +328,7 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
let mut task_contexts = TaskContexts::default();
task_contexts.active_worktree_context = Some((worktree_id, TaskContext::default()));
let task_contexts = Arc::new(task_contexts);
let topmost_local_task_source_kind = TaskSourceKind::Worktree {
id: worktree_id,
@ -354,8 +354,9 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
assert_eq!(settings_a.tab_size.get(), 8);
assert_eq!(settings_b.tab_size.get(), 2);
get_all_tasks(&project, &task_contexts, cx)
get_all_tasks(&project, task_contexts.clone(), cx)
})
.await
.into_iter()
.map(|(source_kind, task)| {
let resolved = task.resolved;
@ -373,7 +374,7 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
(
TaskSourceKind::Worktree {
id: worktree_id,
directory_in_worktree: PathBuf::from(separator!("b/.zed")),
directory_in_worktree: PathBuf::from(path!("b/.zed")),
id_base: if cfg!(windows) {
"local worktree tasks from directory \"b\\\\.zed\"".into()
} else {
@ -394,7 +395,8 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
);
let (_, resolved_task) = cx
.update(|cx| get_all_tasks(&project, &task_contexts, cx))
.update(|cx| get_all_tasks(&project, task_contexts.clone(), cx))
.await
.into_iter()
.find(|(source_kind, _)| source_kind == &topmost_local_task_source_kind)
.expect("should have one global task");
@ -432,7 +434,8 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
cx.run_until_parked();
let all_tasks = cx
.update(|cx| get_all_tasks(&project, &task_contexts, cx))
.update(|cx| get_all_tasks(&project, task_contexts.clone(), cx))
.await
.into_iter()
.map(|(source_kind, task)| {
let resolved = task.resolved;
@ -456,7 +459,7 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
(
TaskSourceKind::Worktree {
id: worktree_id,
directory_in_worktree: PathBuf::from(separator!("b/.zed")),
directory_in_worktree: PathBuf::from(path!("b/.zed")),
id_base: if cfg!(windows) {
"local worktree tasks from directory \"b\\\\.zed\"".into()
} else {
@ -519,55 +522,59 @@ async fn test_fallback_to_single_worktree_tasks(cx: &mut gpui::TestAppContext) {
})
});
let active_non_worktree_item_tasks = cx.update(|cx| {
get_all_tasks(
&project,
&TaskContexts {
active_item_context: Some((Some(worktree_id), None, TaskContext::default())),
active_worktree_context: None,
other_worktree_contexts: Vec::new(),
lsp_task_sources: HashMap::default(),
latest_selection: None,
},
cx,
)
});
let active_non_worktree_item_tasks = cx
.update(|cx| {
get_all_tasks(
&project,
Arc::new(TaskContexts {
active_item_context: Some((Some(worktree_id), None, TaskContext::default())),
active_worktree_context: None,
other_worktree_contexts: Vec::new(),
lsp_task_sources: HashMap::default(),
latest_selection: None,
}),
cx,
)
})
.await;
assert!(
active_non_worktree_item_tasks.is_empty(),
"A task can not be resolved with context with no ZED_WORKTREE_ROOT data"
);
let active_worktree_tasks = cx.update(|cx| {
get_all_tasks(
&project,
&TaskContexts {
active_item_context: Some((Some(worktree_id), None, TaskContext::default())),
active_worktree_context: Some((worktree_id, {
let mut worktree_context = TaskContext::default();
worktree_context
.task_variables
.insert(task::VariableName::WorktreeRoot, "/dir".to_string());
worktree_context
})),
other_worktree_contexts: Vec::new(),
lsp_task_sources: HashMap::default(),
latest_selection: None,
},
cx,
)
});
let active_worktree_tasks = cx
.update(|cx| {
get_all_tasks(
&project,
Arc::new(TaskContexts {
active_item_context: Some((Some(worktree_id), None, TaskContext::default())),
active_worktree_context: Some((worktree_id, {
let mut worktree_context = TaskContext::default();
worktree_context
.task_variables
.insert(task::VariableName::WorktreeRoot, "/dir".to_string());
worktree_context
})),
other_worktree_contexts: Vec::new(),
lsp_task_sources: HashMap::default(),
latest_selection: None,
}),
cx,
)
})
.await;
assert_eq!(
active_worktree_tasks
.into_iter()
.map(|(source_kind, task)| {
let resolved = task.resolved;
(source_kind, resolved.command)
(source_kind, resolved.command.unwrap())
})
.collect::<Vec<_>>(),
vec![(
TaskSourceKind::Worktree {
id: worktree_id,
directory_in_worktree: PathBuf::from(separator!(".zed")),
directory_in_worktree: PathBuf::from(path!(".zed")),
id_base: if cfg!(windows) {
"local worktree tasks from directory \".zed\"".into()
} else {
@ -911,6 +918,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
project.update(cx, |project, cx| {
project.restart_language_servers_for_buffers(
vec![rust_buffer.clone(), json_buffer.clone()],
HashSet::default(),
cx,
);
});
@ -1708,12 +1716,16 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
// Restart the server before the diagnostics finish updating.
project.update(cx, |project, cx| {
project.restart_language_servers_for_buffers(vec![buffer], cx);
project.restart_language_servers_for_buffers(vec![buffer], HashSet::default(), cx);
});
let mut events = cx.events(&project);
// Simulate the newly started server sending more diagnostics.
let fake_server = fake_servers.next().await.unwrap();
assert_eq!(
events.next().await.unwrap(),
Event::LanguageServerRemoved(LanguageServerId(0))
);
assert_eq!(
events.next().await.unwrap(),
Event::LanguageServerAdded(
@ -1813,7 +1825,7 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp
});
project.update(cx, |project, cx| {
project.restart_language_servers_for_buffers(vec![buffer.clone()], cx);
project.restart_language_servers_for_buffers(vec![buffer.clone()], HashSet::default(), cx);
});
// The diagnostics are cleared.
@ -1868,7 +1880,7 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T
});
cx.executor().run_until_parked();
project.update(cx, |project, cx| {
project.restart_language_servers_for_buffers(vec![buffer.clone()], cx);
project.restart_language_servers_for_buffers(vec![buffer.clone()], HashSet::default(), cx);
});
let mut fake_server = fake_servers.next().await.unwrap();
@ -2011,7 +2023,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
cx.update(|cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
settings.languages.insert(
settings.languages.0.insert(
"Rust".into(),
LanguageSettingsContent {
enable_language_server: Some(false),
@ -2030,14 +2042,14 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
cx.update(|cx| {
SettingsStore::update_global(cx, |settings, cx| {
settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
settings.languages.insert(
settings.languages.0.insert(
LanguageName::new("Rust"),
LanguageSettingsContent {
enable_language_server: Some(true),
..Default::default()
},
);
settings.languages.insert(
settings.languages.0.insert(
LanguageName::new("JavaScript"),
LanguageSettingsContent {
enable_language_server: Some(false),
@ -2981,7 +2993,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
)))
});
let mut definitions = project
.update(cx, |project, cx| project.definition(&buffer, 22, cx))
.update(cx, |project, cx| project.definitions(&buffer, 22, cx))
.await
.unwrap();
@ -3979,12 +3991,12 @@ async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) {
.collect::<Vec<_>>(),
vec![
"a",
separator!("a/file1"),
separator!("a/file2.new"),
path!("a/file1"),
path!("a/file2.new"),
"b",
"d",
separator!("d/file3"),
separator!("d/file4"),
path!("d/file3"),
path!("d/file4"),
]
);
});
@ -4047,12 +4059,12 @@ async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) {
.collect::<Vec<_>>(),
vec![
"a",
separator!("a/file1"),
separator!("a/file2.new"),
path!("a/file1"),
path!("a/file2.new"),
"b",
"d",
separator!("d/file3"),
separator!("d/file4"),
path!("d/file3"),
path!("d/file4"),
]
);
});
@ -5047,8 +5059,8 @@ async fn test_search(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/two.rs").to_string(), vec![6..9]),
(separator!("dir/three.rs").to_string(), vec![37..40])
(path!("dir/two.rs").to_string(), vec![6..9]),
(path!("dir/three.rs").to_string(), vec![37..40])
])
);
@ -5082,9 +5094,9 @@ async fn test_search(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/two.rs").to_string(), vec![6..9]),
(separator!("dir/three.rs").to_string(), vec![37..40]),
(separator!("dir/four.rs").to_string(), vec![25..28, 36..39])
(path!("dir/two.rs").to_string(), vec![6..9]),
(path!("dir/three.rs").to_string(), vec![37..40]),
(path!("dir/four.rs").to_string(), vec![25..28, 36..39])
])
);
}
@ -5149,8 +5161,8 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/one.rs").to_string(), vec![8..12]),
(separator!("dir/two.rs").to_string(), vec![8..12]),
(path!("dir/one.rs").to_string(), vec![8..12]),
(path!("dir/two.rs").to_string(), vec![8..12]),
]),
"Rust only search should give only Rust files"
);
@ -5174,8 +5186,8 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/one.ts").to_string(), vec![14..18]),
(separator!("dir/two.ts").to_string(), vec![14..18]),
(path!("dir/one.ts").to_string(), vec![14..18]),
(path!("dir/two.ts").to_string(), vec![14..18]),
]),
"TypeScript only search should give only TypeScript files, even if other inclusions don't match anything"
);
@ -5200,10 +5212,10 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/two.ts").to_string(), vec![14..18]),
(separator!("dir/one.rs").to_string(), vec![8..12]),
(separator!("dir/one.ts").to_string(), vec![14..18]),
(separator!("dir/two.rs").to_string(), vec![8..12]),
(path!("dir/two.ts").to_string(), vec![14..18]),
(path!("dir/one.rs").to_string(), vec![8..12]),
(path!("dir/one.ts").to_string(), vec![14..18]),
(path!("dir/two.rs").to_string(), vec![8..12]),
]),
"Rust and typescript search should give both Rust and TypeScript files, even if other inclusions don't match anything"
);
@ -5247,10 +5259,10 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/one.rs").to_string(), vec![8..12]),
(separator!("dir/one.ts").to_string(), vec![14..18]),
(separator!("dir/two.rs").to_string(), vec![8..12]),
(separator!("dir/two.ts").to_string(), vec![14..18]),
(path!("dir/one.rs").to_string(), vec![8..12]),
(path!("dir/one.ts").to_string(), vec![14..18]),
(path!("dir/two.rs").to_string(), vec![8..12]),
(path!("dir/two.ts").to_string(), vec![14..18]),
]),
"If no exclusions match, all files should be returned"
);
@ -5274,8 +5286,8 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/one.ts").to_string(), vec![14..18]),
(separator!("dir/two.ts").to_string(), vec![14..18]),
(path!("dir/one.ts").to_string(), vec![14..18]),
(path!("dir/two.ts").to_string(), vec![14..18]),
]),
"Rust exclusion search should give only TypeScript files"
);
@ -5299,8 +5311,134 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/one.rs").to_string(), vec![8..12]),
(separator!("dir/two.rs").to_string(), vec![8..12]),
(path!("dir/one.rs").to_string(), vec![8..12]),
(path!("dir/two.rs").to_string(), vec![8..12]),
]),
"TypeScript exclusion search should give only Rust files, even if other exclusions don't match anything"
);
assert!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
false,
Default::default(),
PathMatcher::new(&["*.rs".to_owned(), "*.ts".to_owned(), "*.odd".to_owned()])
.unwrap(),
false,
None,
)
.unwrap(),
cx
)
.await
.unwrap()
.is_empty(),
"Rust and typescript exclusion should give no files, even if other exclusions don't match anything"
);
}
#[gpui::test]
async fn test_search_with_buffer_exclusions(cx: &mut gpui::TestAppContext) {
init_test(cx);
let search_query = "file";
let fs = FakeFs::new(cx.executor());
fs.insert_tree(
path!("/dir"),
json!({
"one.rs": r#"// Rust file one"#,
"one.ts": r#"// TypeScript file one"#,
"two.rs": r#"// Rust file two"#,
"two.ts": r#"// TypeScript file two"#,
}),
)
.await;
let project = Project::test(fs.clone(), [path!("/dir").as_ref()], cx).await;
let _buffer = project.update(cx, |project, cx| {
let buffer = project.create_local_buffer("file", None, cx);
project.mark_buffer_as_non_searchable(buffer.read(cx).remote_id(), cx);
buffer
});
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
false,
Default::default(),
PathMatcher::new(&["*.odd".to_owned()]).unwrap(),
false,
None,
)
.unwrap(),
cx
)
.await
.unwrap(),
HashMap::from_iter([
(path!("dir/one.rs").to_string(), vec![8..12]),
(path!("dir/one.ts").to_string(), vec![14..18]),
(path!("dir/two.rs").to_string(), vec![8..12]),
(path!("dir/two.ts").to_string(), vec![14..18]),
]),
"If no exclusions match, all files should be returned"
);
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
false,
Default::default(),
PathMatcher::new(&["*.rs".to_owned()]).unwrap(),
false,
None,
)
.unwrap(),
cx
)
.await
.unwrap(),
HashMap::from_iter([
(path!("dir/one.ts").to_string(), vec![14..18]),
(path!("dir/two.ts").to_string(), vec![14..18]),
]),
"Rust exclusion search should give only TypeScript files"
);
assert_eq!(
search(
&project,
SearchQuery::text(
search_query,
false,
true,
false,
Default::default(),
PathMatcher::new(&["*.ts".to_owned(), "*.odd".to_owned()]).unwrap(),
false,
None,
)
.unwrap(),
cx
)
.await
.unwrap(),
HashMap::from_iter([
(path!("dir/one.rs").to_string(), vec![8..12]),
(path!("dir/two.rs").to_string(), vec![8..12]),
]),
"TypeScript exclusion search should give only Rust files, even if other exclusions don't match anything"
);
@ -5433,8 +5571,8 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/one.ts").to_string(), vec![14..18]),
(separator!("dir/two.ts").to_string(), vec![14..18]),
(path!("dir/one.ts").to_string(), vec![14..18]),
(path!("dir/two.ts").to_string(), vec![14..18]),
]),
"Non-intersecting TypeScript inclusions and Rust exclusions should return TypeScript files"
);
@ -5487,7 +5625,7 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo
)
.await
.unwrap(),
HashMap::from_iter([(separator!("worktree-a/haystack.rs").to_string(), vec![3..9])]),
HashMap::from_iter([(path!("worktree-a/haystack.rs").to_string(), vec![3..9])]),
"should only return results from included worktree"
);
assert_eq!(
@ -5508,7 +5646,7 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo
)
.await
.unwrap(),
HashMap::from_iter([(separator!("worktree-b/haystack.rs").to_string(), vec![3..9])]),
HashMap::from_iter([(path!("worktree-b/haystack.rs").to_string(), vec![3..9])]),
"should only return results from included worktree"
);
@ -5531,8 +5669,8 @@ async fn test_search_multiple_worktrees_with_inclusions(cx: &mut gpui::TestAppCo
.await
.unwrap(),
HashMap::from_iter([
(separator!("worktree-a/haystack.ts").to_string(), vec![3..9]),
(separator!("worktree-b/haystack.ts").to_string(), vec![3..9])
(path!("worktree-a/haystack.ts").to_string(), vec![3..9]),
(path!("worktree-b/haystack.ts").to_string(), vec![3..9])
]),
"should return results from both worktrees"
);
@ -5586,7 +5724,7 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) {
)
.await
.unwrap(),
HashMap::from_iter([(separator!("dir/package.json").to_string(), vec![8..11])]),
HashMap::from_iter([(path!("dir/package.json").to_string(), vec![8..11])]),
"Only one non-ignored file should have the query"
);
@ -5610,22 +5748,22 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/package.json").to_string(), vec![8..11]),
(separator!("dir/target/index.txt").to_string(), vec![6..9]),
(path!("dir/package.json").to_string(), vec![8..11]),
(path!("dir/target/index.txt").to_string(), vec![6..9]),
(
separator!("dir/node_modules/prettier/package.json").to_string(),
path!("dir/node_modules/prettier/package.json").to_string(),
vec![9..12]
),
(
separator!("dir/node_modules/prettier/index.ts").to_string(),
path!("dir/node_modules/prettier/index.ts").to_string(),
vec![15..18]
),
(
separator!("dir/node_modules/eslint/index.ts").to_string(),
path!("dir/node_modules/eslint/index.ts").to_string(),
vec![13..16]
),
(
separator!("dir/node_modules/eslint/package.json").to_string(),
path!("dir/node_modules/eslint/package.json").to_string(),
vec![8..11]
),
]),
@ -5654,7 +5792,7 @@ async fn test_search_in_gitignored_dirs(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([(
separator!("dir/node_modules/prettier/package.json").to_string(),
path!("dir/node_modules/prettier/package.json").to_string(),
vec![9..12]
)]),
"With search including ignored prettier directory and excluding TS files, only one file should be found"
@ -5693,8 +5831,8 @@ async fn test_search_with_unicode(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/one.rs").to_string(), vec![17..29]),
(separator!("dir/three.rs").to_string(), vec![3..15]),
(path!("dir/one.rs").to_string(), vec![17..29]),
(path!("dir/three.rs").to_string(), vec![3..15]),
])
);
@ -5717,9 +5855,9 @@ async fn test_search_with_unicode(cx: &mut gpui::TestAppContext) {
.await
.unwrap(),
HashMap::from_iter([
(separator!("dir/one.rs").to_string(), vec![3..15, 17..29]),
(separator!("dir/two.rs").to_string(), vec![3..15]),
(separator!("dir/three.rs").to_string(), vec![3..15]),
(path!("dir/one.rs").to_string(), vec![3..15, 17..29]),
(path!("dir/two.rs").to_string(), vec![3..15]),
(path!("dir/three.rs").to_string(), vec![3..15]),
])
);
@ -5741,7 +5879,7 @@ async fn test_search_with_unicode(cx: &mut gpui::TestAppContext) {
)
.await
.unwrap(),
HashMap::from_iter([(separator!("dir/two.rs").to_string(), vec![3..16]),])
HashMap::from_iter([(path!("dir/two.rs").to_string(), vec![3..16]),])
);
}
@ -7364,13 +7502,13 @@ async fn test_staging_random_hunks(
if hunk.status().has_secondary_hunk() {
log::info!("staging hunk at {row}");
uncommitted_diff.update(cx, |diff, cx| {
diff.stage_or_unstage_hunks(true, &[hunk.clone()], &snapshot, true, cx);
diff.stage_or_unstage_hunks(true, std::slice::from_ref(hunk), &snapshot, true, cx);
});
hunk.secondary_status = SecondaryHunkRemovalPending;
} else {
log::info!("unstaging hunk at {row}");
uncommitted_diff.update(cx, |diff, cx| {
diff.stage_or_unstage_hunks(false, &[hunk.clone()], &snapshot, true, cx);
diff.stage_or_unstage_hunks(false, std::slice::from_ref(hunk), &snapshot, true, cx);
});
hunk.secondary_status = SecondaryHunkAdditionPending;
}
@ -8850,20 +8988,22 @@ fn tsx_lang() -> Arc<Language> {
fn get_all_tasks(
project: &Entity<Project>,
task_contexts: &TaskContexts,
task_contexts: Arc<TaskContexts>,
cx: &mut App,
) -> Vec<(TaskSourceKind, ResolvedTask)> {
let (mut old, new) = project.update(cx, |project, cx| {
project
.task_store
.read(cx)
.task_inventory()
.unwrap()
.read(cx)
.used_and_current_resolved_tasks(task_contexts, cx)
) -> Task<Vec<(TaskSourceKind, ResolvedTask)>> {
let new_tasks = project.update(cx, |project, cx| {
project.task_store.update(cx, |task_store, cx| {
task_store.task_inventory().unwrap().update(cx, |this, cx| {
this.used_and_current_resolved_tasks(task_contexts, cx)
})
})
});
old.extend(new);
old
cx.background_spawn(async move {
let (mut old, new) = new_tasks.await;
old.extend(new);
old
})
}
#[track_caller]