editor: Add access method for project (#36266)

This resolves a `TODO` that I've stumbled upon too many times whilst
looking at the editor code.

Release Notes:

- N/A
This commit is contained in:
Finn Evers 2025-08-15 20:34:22 +02:00 committed by GitHub
parent bd1fda6782
commit 3c5d5a1d57
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 42 additions and 45 deletions

View file

@ -971,7 +971,7 @@ async fn active_diagnostics_dismiss_after_invalidation(cx: &mut TestAppContext)
let mut cx = EditorTestContext::new(cx).await;
let lsp_store =
cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
cx.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).lsp_store());
cx.set_state(indoc! {"
ˇfn func(abc def: i32) -> u32 {
@ -1065,7 +1065,7 @@ async fn cycle_through_same_place_diagnostics(cx: &mut TestAppContext) {
let mut cx = EditorTestContext::new(cx).await;
let lsp_store =
cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
cx.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).lsp_store());
cx.set_state(indoc! {"
ˇfn func(abc def: i32) -> u32 {
@ -1239,7 +1239,7 @@ async fn test_diagnostics_with_links(cx: &mut TestAppContext) {
}
"});
let lsp_store =
cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
cx.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).lsp_store());
cx.update(|_, cx| {
lsp_store.update(cx, |lsp_store, cx| {
@ -1293,7 +1293,7 @@ async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext)
fn «test»() { println!(); }
"});
let lsp_store =
cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
cx.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).lsp_store());
cx.update(|_, cx| {
lsp_store.update(cx, |lsp_store, cx| {
lsp_store.update_diagnostics(
@ -1450,7 +1450,7 @@ async fn go_to_diagnostic_with_severity(cx: &mut TestAppContext) {
let mut cx = EditorTestContext::new(cx).await;
let lsp_store =
cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
cx.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).lsp_store());
cx.set_state(indoc! {"error warning info hiˇnt"});

View file

@ -1039,9 +1039,7 @@ pub struct Editor {
inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
soft_wrap_mode_override: Option<language_settings::SoftWrap>,
hard_wrap: Option<usize>,
// TODO: make this a access method
pub project: Option<Entity<Project>>,
project: Option<Entity<Project>>,
semantics_provider: Option<Rc<dyn SemanticsProvider>>,
completion_provider: Option<Rc<dyn CompletionProvider>>,
collaboration_hub: Option<Box<dyn CollaborationHub>>,
@ -2326,7 +2324,7 @@ impl Editor {
editor.go_to_active_debug_line(window, cx);
if let Some(buffer) = buffer.read(cx).as_singleton() {
if let Some(project) = editor.project.as_ref() {
if let Some(project) = editor.project() {
let handle = project.update(cx, |project, cx| {
project.register_buffer_with_language_servers(&buffer, cx)
});
@ -2626,6 +2624,10 @@ impl Editor {
&self.buffer
}
pub fn project(&self) -> Option<&Entity<Project>> {
self.project.as_ref()
}
pub fn workspace(&self) -> Option<Entity<Workspace>> {
self.workspace.as_ref()?.0.upgrade()
}
@ -5212,7 +5214,7 @@ impl Editor {
restrict_to_languages: Option<&HashSet<Arc<Language>>>,
cx: &mut Context<Editor>,
) -> HashMap<ExcerptId, (Entity<Buffer>, clock::Global, Range<usize>)> {
let Some(project) = self.project.as_ref() else {
let Some(project) = self.project() else {
return HashMap::default();
};
let project = project.read(cx);
@ -5294,7 +5296,7 @@ impl Editor {
return None;
}
let project = self.project.as_ref()?;
let project = self.project()?;
let position = self.selections.newest_anchor().head();
let (buffer, buffer_position) = self
.buffer
@ -6141,7 +6143,7 @@ impl Editor {
cx: &mut App,
) -> Task<Vec<task::DebugScenario>> {
maybe!({
let project = self.project.as_ref()?;
let project = self.project()?;
let dap_store = project.read(cx).dap_store();
let mut scenarios = vec![];
let resolved_tasks = resolved_tasks.as_ref()?;
@ -7907,7 +7909,7 @@ impl Editor {
let snapshot = self.snapshot(window, cx);
let multi_buffer_snapshot = &snapshot.display_snapshot.buffer_snapshot;
let Some(project) = self.project.as_ref() else {
let Some(project) = self.project() else {
return breakpoint_display_points;
};
@ -10501,7 +10503,7 @@ impl Editor {
) {
if let Some(working_directory) = self.active_excerpt(cx).and_then(|(_, buffer, _)| {
let project_path = buffer.read(cx).project_path(cx)?;
let project = self.project.as_ref()?.read(cx);
let project = self.project()?.read(cx);
let entry = project.entry_for_path(&project_path, cx)?;
let parent = match &entry.canonical_path {
Some(canonical_path) => canonical_path.to_path_buf(),
@ -14875,7 +14877,7 @@ impl Editor {
self.clear_tasks();
return Task::ready(());
}
let project = self.project.as_ref().map(Entity::downgrade);
let project = self.project().map(Entity::downgrade);
let task_sources = self.lsp_task_sources(cx);
let multi_buffer = self.buffer.downgrade();
cx.spawn_in(window, async move |editor, cx| {
@ -17054,7 +17056,7 @@ impl Editor {
if !pull_diagnostics_settings.enabled {
return None;
}
let project = self.project.as_ref()?.downgrade();
let project = self.project()?.downgrade();
let debounce = Duration::from_millis(pull_diagnostics_settings.debounce_ms);
let mut buffers = self.buffer.read(cx).all_buffers();
if let Some(buffer_id) = buffer_id {
@ -18018,7 +18020,7 @@ impl Editor {
hunks: impl Iterator<Item = MultiBufferDiffHunk>,
cx: &mut App,
) -> Option<()> {
let project = self.project.as_ref()?;
let project = self.project()?;
let buffer = project.read(cx).buffer_for_id(buffer_id, cx)?;
let diff = self.buffer.read(cx).diff_for(buffer_id)?;
let buffer_snapshot = buffer.read(cx).snapshot();
@ -18678,7 +18680,7 @@ impl Editor {
self.active_excerpt(cx).and_then(|(_, buffer, _)| {
let buffer = buffer.read(cx);
if let Some(project_path) = buffer.project_path(cx) {
let project = self.project.as_ref()?.read(cx);
let project = self.project()?.read(cx);
project.absolute_path(&project_path, cx)
} else {
buffer
@ -18691,7 +18693,7 @@ impl Editor {
fn target_file_path(&self, cx: &mut Context<Self>) -> Option<PathBuf> {
self.active_excerpt(cx).and_then(|(_, buffer, _)| {
let project_path = buffer.read(cx).project_path(cx)?;
let project = self.project.as_ref()?.read(cx);
let project = self.project()?.read(cx);
let entry = project.entry_for_path(&project_path, cx)?;
let path = entry.path.to_path_buf();
Some(path)
@ -18912,7 +18914,7 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(project) = self.project.as_ref() {
if let Some(project) = self.project() {
let Some(buffer) = self.buffer().read(cx).as_singleton() else {
return;
};
@ -19028,7 +19030,7 @@ impl Editor {
return Task::ready(Err(anyhow!("failed to determine buffer and selection")));
};
let Some(project) = self.project.as_ref() else {
let Some(project) = self.project() else {
return Task::ready(Err(anyhow!("editor does not have project")));
};
@ -21015,7 +21017,7 @@ impl Editor {
cx: &mut Context<Self>,
) {
let workspace = self.workspace();
let project = self.project.as_ref();
let project = self.project();
let save_tasks = self.buffer().update(cx, |multi_buffer, cx| {
let mut tasks = Vec::new();
for (buffer_id, changes) in revert_changes {

View file

@ -15082,7 +15082,7 @@ async fn go_to_prev_overlapping_diagnostic(executor: BackgroundExecutor, cx: &mu
let mut cx = EditorTestContext::new(cx).await;
let lsp_store =
cx.update_editor(|editor, _, cx| editor.project.as_ref().unwrap().read(cx).lsp_store());
cx.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).lsp_store());
cx.set_state(indoc! {"
ˇfn func(abc def: i32) -> u32 {

View file

@ -251,7 +251,7 @@ fn show_hover(
let (excerpt_id, _, _) = editor.buffer().read(cx).excerpt_containing(anchor, cx)?;
let language_registry = editor.project.as_ref()?.read(cx).languages().clone();
let language_registry = editor.project()?.read(cx).languages().clone();
let provider = editor.semantics_provider.clone()?;
if !ignore_timeout {

View file

@ -678,7 +678,7 @@ impl Item for Editor {
let buffer = buffer.read(cx);
let path = buffer.project_path(cx)?;
let buffer_id = buffer.remote_id();
let project = self.project.as_ref()?.read(cx);
let project = self.project()?.read(cx);
let entry = project.entry_for_path(&path, cx)?;
let (repo, repo_path) = project
.git_store()

View file

@ -51,7 +51,7 @@ pub(super) fn refresh_linked_ranges(
if editor.pending_rename.is_some() {
return None;
}
let project = editor.project.as_ref()?.downgrade();
let project = editor.project()?.downgrade();
editor.linked_editing_range_task = Some(cx.spawn_in(window, async move |editor, cx| {
cx.background_executor().timer(UPDATE_DEBOUNCE).await;

View file

@ -169,7 +169,7 @@ impl Editor {
else {
return;
};
let Some(lsp_store) = self.project.as_ref().map(|p| p.read(cx).lsp_store()) else {
let Some(lsp_store) = self.project().map(|p| p.read(cx).lsp_store()) else {
return;
};
let task = lsp_store.update(cx, |lsp_store, cx| {

View file

@ -297,9 +297,8 @@ impl EditorTestContext {
pub fn set_head_text(&mut self, diff_base: &str) {
self.cx.run_until_parked();
let fs = self.update_editor(|editor, _, cx| {
editor.project.as_ref().unwrap().read(cx).fs().as_fake()
});
let fs =
self.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).fs().as_fake());
let path = self.update_buffer(|buffer, _| buffer.file().unwrap().path().clone());
fs.set_head_for_repo(
&Self::root_path().join(".git"),
@ -311,18 +310,16 @@ impl EditorTestContext {
pub fn clear_index_text(&mut self) {
self.cx.run_until_parked();
let fs = self.update_editor(|editor, _, cx| {
editor.project.as_ref().unwrap().read(cx).fs().as_fake()
});
let fs =
self.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).fs().as_fake());
fs.set_index_for_repo(&Self::root_path().join(".git"), &[]);
self.cx.run_until_parked();
}
pub fn set_index_text(&mut self, diff_base: &str) {
self.cx.run_until_parked();
let fs = self.update_editor(|editor, _, cx| {
editor.project.as_ref().unwrap().read(cx).fs().as_fake()
});
let fs =
self.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).fs().as_fake());
let path = self.update_buffer(|buffer, _| buffer.file().unwrap().path().clone());
fs.set_index_for_repo(
&Self::root_path().join(".git"),
@ -333,9 +330,8 @@ impl EditorTestContext {
#[track_caller]
pub fn assert_index_text(&mut self, expected: Option<&str>) {
let fs = self.update_editor(|editor, _, cx| {
editor.project.as_ref().unwrap().read(cx).fs().as_fake()
});
let fs =
self.update_editor(|editor, _, cx| editor.project().unwrap().read(cx).fs().as_fake());
let path = self.update_buffer(|buffer, _| buffer.file().unwrap().path().clone());
let mut found = None;
fs.with_git_state(&Self::root_path().join(".git"), false, |git_state| {

View file

@ -112,7 +112,7 @@ fn excerpt_for_buffer_updated(
}
fn buffer_added(editor: &mut Editor, buffer: Entity<Buffer>, cx: &mut Context<Editor>) {
let Some(project) = &editor.project else {
let Some(project) = editor.project() else {
return;
};
let git_store = project.read(cx).git_store().clone();
@ -469,7 +469,7 @@ pub(crate) fn resolve_conflict(
let Some((workspace, project, multibuffer, buffer)) = editor
.update(cx, |editor, cx| {
let workspace = editor.workspace()?;
let project = editor.project.clone()?;
let project = editor.project()?.clone();
let multibuffer = editor.buffer().clone();
let buffer_id = resolved_conflict.ours.end.buffer_id?;
let buffer = multibuffer.read(cx).buffer(buffer_id)?;

View file

@ -299,7 +299,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
Vim::action(editor, cx, |vim, action: &VimSave, window, cx| {
vim.update_editor(cx, |_, editor, cx| {
let Some(project) = editor.project.clone() else {
let Some(project) = editor.project().cloned() else {
return;
};
let Some(worktree) = project.read(cx).visible_worktrees(cx).next() else {
@ -436,7 +436,7 @@ pub fn register(editor: &mut Editor, cx: &mut Context<Vim>) {
let Some(workspace) = vim.workspace(window) else {
return;
};
let Some(project) = editor.project.clone() else {
let Some(project) = editor.project().cloned() else {
return;
};
let Some(worktree) = project.read(cx).visible_worktrees(cx).next() else {

View file

@ -229,8 +229,7 @@ fn assign_edit_prediction_provider(
if let Some(file) = buffer.read(cx).file() {
let id = file.worktree_id(cx);
if let Some(inner_worktree) = editor
.project
.as_ref()
.project()
.and_then(|project| project.read(cx).worktree_for_id(id, cx))
{
worktree = Some(inner_worktree);