settings_ui: Enable editing project settings for worktrees without setting file (#40971)

I made three significant changes in this PR. 

1. `SettingsWindow::fetch_files` now creates
`SettingsUiFile::Project(..)` for any worktree that contains no project
settings.
2. `update_settings_file` now creates an empty settings file if a
worktree doesn't contain one.
3. `open_current_settings_file` also creates a settings file if the
current one doesn't exist.

Release Notes:

- settings ui: Enable editing project settings for worktrees that don't
have a project setting file.
This commit is contained in:
Anthony Eid
2025-10-23 02:23:12 -04:00
committed by GitHub
parent 1edb1b3896
commit 05c2cc0254
+130 -48
View File
@@ -1745,7 +1745,41 @@ impl SettingsWindow {
.unwrap_or_else(|| cx.focus_handle().tab_index(0).tab_stop(true));
ui_files.push((settings_ui_file, focus_handle));
}
ui_files.reverse();
let mut missing_worktrees = Vec::new();
for worktree in all_projects(cx)
.flat_map(|project| project.read(cx).worktrees(cx))
.filter(|tree| !self.worktree_root_dirs.contains_key(&tree.read(cx).id()))
{
let worktree = worktree.read(cx);
let worktree_id = worktree.id();
let Some(directory_name) = worktree.root_dir().and_then(|file| {
file.file_name()
.map(|os_string| os_string.to_string_lossy().to_string())
}) else {
continue;
};
missing_worktrees.push((worktree_id, directory_name.clone()));
let path = RelPath::empty().to_owned().into_arc();
let settings_ui_file = SettingsUiFile::Project((worktree_id, path));
let focus_handle = prev_files
.iter()
.find_map(|(prev_file, handle)| {
(prev_file == &settings_ui_file).then(|| handle.clone())
})
.unwrap_or_else(|| cx.focus_handle().tab_index(0).tab_stop(true));
ui_files.push((settings_ui_file, focus_handle));
}
self.worktree_root_dirs.extend(missing_worktrees);
self.files = ui_files;
let current_file_still_exists = self
.files
@@ -2718,6 +2752,9 @@ impl SettingsWindow {
);
}
/// This function will create a new settings file if one doesn't exist
/// if the current file is a project settings with a valid worktree id
/// We do this because the settings ui allows initializing project settings
fn open_current_settings_file(&mut self, cx: &mut Context<Self>) {
match &self.current_file {
SettingsUiFile::User => {
@@ -2762,58 +2799,83 @@ impl SettingsWindow {
.ok();
}
SettingsUiFile::Project((worktree_id, path)) => {
let mut corresponding_workspace: Option<WindowHandle<Workspace>> = None;
let settings_path = path.join(paths::local_settings_file_relative_path());
let Some(app_state) = workspace::AppState::global(cx).upgrade() else {
return;
};
for workspace in app_state.workspace_store.read(cx).workspaces() {
let contains_settings_file = workspace
.read_with(cx, |workspace, cx| {
workspace.project().read(cx).contains_local_settings_file(
*worktree_id,
settings_path.as_ref(),
cx,
)
})
.ok();
if Some(true) == contains_settings_file {
corresponding_workspace = Some(*workspace);
break;
}
}
let Some(corresponding_workspace) = corresponding_workspace else {
let Some((worktree, corresponding_workspace)) = app_state
.workspace_store
.read(cx)
.workspaces()
.iter()
.find_map(|workspace| {
workspace
.read_with(cx, |workspace, cx| {
workspace
.project()
.read(cx)
.worktree_for_id(*worktree_id, cx)
})
.ok()
.flatten()
.zip(Some(*workspace))
})
else {
log::error!(
"No corresponding workspace found for settings file {}",
settings_path.as_std_path().display()
"No corresponding workspace contains worktree id: {}",
worktree_id
);
return;
};
let create_task = if worktree.read(cx).entry_for_path(&settings_path).is_some() {
None
} else {
Some(worktree.update(cx, |tree, cx| {
tree.create_entry(
settings_path.clone(),
false,
Some("{\n\n}".as_bytes().to_vec()),
cx,
)
}))
};
let worktree_id = *worktree_id;
// TODO: move zed::open_local_file() APIs to this crate, and
// re-implement the "initial_contents" behavior
corresponding_workspace
.update(cx, |workspace, window, cx| {
let open_task = workspace.open_path(
(*worktree_id, settings_path.clone()),
None,
true,
window,
cx,
);
.update(cx, |_, window, cx| {
cx.spawn_in(window, async move |workspace, cx| {
if open_task.await.log_err().is_some() {
workspace
.update_in(cx, |_, window, cx| {
window.activate_window();
cx.notify();
})
.ok();
}
if let Some(create_task) = create_task {
create_task.await.ok()?;
};
workspace
.update_in(cx, |workspace, window, cx| {
workspace.open_path(
(worktree_id, settings_path.clone()),
None,
true,
window,
cx,
)
})
.ok()?
.await
.log_err()?;
workspace
.update_in(cx, |_, window, cx| {
window.activate_window();
cx.notify();
})
.ok();
Some(())
})
.detach();
})
@@ -3033,20 +3095,40 @@ fn update_settings_file(
match file {
SettingsUiFile::Project((worktree_id, rel_path)) => {
let rel_path = rel_path.join(paths::local_settings_file_relative_path());
let project = all_projects(cx).find(|project| {
project.read_with(cx, |project, cx| {
project.contains_local_settings_file(worktree_id, &rel_path, cx)
})
});
let Some(project) = project else {
anyhow::bail!(
"Could not find worktree containing settings file: {}",
&rel_path.display(PathStyle::local())
);
let Some((worktree, project)) = all_projects(cx).find_map(|project| {
project
.read(cx)
.worktree_for_id(worktree_id, cx)
.zip(Some(project))
}) else {
anyhow::bail!("Could not find project with worktree id: {}", worktree_id);
};
project.update(cx, |project, cx| {
project.update_local_settings_file(worktree_id, rel_path, cx, update);
let task = if project.contains_local_settings_file(worktree_id, &rel_path, cx) {
None
} else {
Some(worktree.update(cx, |worktree, cx| {
worktree.create_entry(rel_path.clone(), false, None, cx)
}))
};
cx.spawn(async move |project, cx| {
if let Some(task) = task
&& task.await.is_err()
{
return;
};
project
.update(cx, |project, cx| {
project.update_local_settings_file(worktree_id, rel_path, cx, update);
})
.ok();
})
.detach();
});
return Ok(());
}
SettingsUiFile::User => {