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:
@@ -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 => {
|
||||
|
||||
Reference in New Issue
Block a user