diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index 804cf1ad8d12771266b1104dff7b3fc1b1cf97d4..c08e00ffbe483846106e1f2de2b86de5c1bb5eb9 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -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) { match &self.current_file { SettingsUiFile::User => { @@ -2762,58 +2799,83 @@ impl SettingsWindow { .ok(); } SettingsUiFile::Project((worktree_id, path)) => { - let mut corresponding_workspace: Option> = 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 => {