Reuse workspace on new journal entry command if possible (#16924)

ZZzzaaKK created

Closes #6783

With this PR, the `journal: new journal entry` command only opens a new
workspace if the current workspace does not already contain the
`journal` directory. Both the root of the work tree and all its
subdirectories are checked.

This does not yet check for the day's file specifically, as suggested
[here](https://github.com/zed-industries/zed/issues/6783#issuecomment-2268509463).

I'm new to writing Rust code in production (as well as contributing in
general), so any feedback is much appreciated!

Release Notes:

- Reuse workspace on `journal: new journal entry` command if possible

Change summary

crates/journal/src/journal.rs | 64 ++++++++++++++++++++++++++----------
1 file changed, 46 insertions(+), 18 deletions(-)

Detailed changes

crates/journal/src/journal.rs 🔗

@@ -61,14 +61,14 @@ pub fn init(_: Arc<AppState>, cx: &mut AppContext) {
     cx.observe_new_views(
         |workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>| {
             workspace.register_action(|workspace, _: &NewJournalEntry, cx| {
-                new_journal_entry(workspace.app_state().clone(), cx);
+                new_journal_entry(&workspace, cx);
             });
         },
     )
     .detach();
 }
 
-pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut WindowContext) {
+pub fn new_journal_entry(workspace: &Workspace, cx: &mut WindowContext) {
     let settings = JournalSettings::get_global(cx);
     let journal_dir = match journal_dir(settings.path.as_ref().unwrap()) {
         Some(journal_dir) => journal_dir,
@@ -77,6 +77,7 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut WindowContext) {
             return;
         }
     };
+    let journal_dir_clone = journal_dir.clone();
 
     let now = Local::now();
     let month_dir = journal_dir
@@ -96,24 +97,51 @@ pub fn new_journal_entry(app_state: Arc<AppState>, cx: &mut WindowContext) {
         Ok::<_, std::io::Error>((journal_dir, entry_path))
     });
 
+    let worktrees = workspace.visible_worktrees(cx).collect::<Vec<_>>();
+    let mut open_new_workspace = true;
+    'outer: for worktree in worktrees.iter() {
+        let worktree_root = worktree.read(cx).abs_path();
+        if *worktree_root == journal_dir_clone {
+            open_new_workspace = false;
+            break;
+        }
+        for directory in worktree.read(cx).directories(true, 1) {
+            let full_directory_path = worktree_root.join(&directory.path);
+            if full_directory_path.ends_with(&journal_dir_clone) {
+                open_new_workspace = false;
+                break 'outer;
+            }
+        }
+    }
+
+    let app_state = workspace.app_state().clone();
+    let view_snapshot = workspace.weak_handle().clone();
+
     cx.spawn(|mut cx| async move {
         let (journal_dir, entry_path) = create_entry.await?;
-        let (workspace, _) = cx
-            .update(|cx| {
-                workspace::open_paths(
-                    &[journal_dir],
-                    app_state,
-                    workspace::OpenOptions::default(),
-                    cx,
-                )
-            })?
-            .await?;
-
-        let opened = workspace
-            .update(&mut cx, |workspace, cx| {
-                workspace.open_paths(vec![entry_path], OpenVisible::All, None, cx)
-            })?
-            .await;
+        let opened = if open_new_workspace {
+            let (new_workspace, _) = cx
+                .update(|cx| {
+                    workspace::open_paths(
+                        &[journal_dir],
+                        app_state,
+                        workspace::OpenOptions::default(),
+                        cx,
+                    )
+                })?
+                .await?;
+            new_workspace
+                .update(&mut cx, |workspace, cx| {
+                    workspace.open_paths(vec![entry_path], OpenVisible::All, None, cx)
+                })?
+                .await
+        } else {
+            view_snapshot
+                .update(&mut cx, |workspace, cx| {
+                    workspace.open_paths(vec![entry_path], OpenVisible::All, None, cx)
+                })?
+                .await
+        };
 
         if let Some(Some(Ok(item))) = opened.first() {
             if let Some(editor) = item.downcast::<Editor>().map(|editor| editor.downgrade()) {