editor: Add ToggleFocus action (#34495)

Smit Barmase and Danilo Leal created

This PR adds action `editor: toggle focus` which focuses to last active
editor pane item in workspace.

Release Notes:

- Added `editor: toggle focus` action, which focuses to last active
editor pane item.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>

Change summary

crates/editor/src/actions.rs      |  2 ++
crates/editor/src/editor.rs       | 13 +++++++++++++
crates/workspace/src/workspace.rs | 21 +++++++++++++++++++++
3 files changed, 36 insertions(+)

Detailed changes

crates/editor/src/actions.rs 🔗

@@ -425,6 +425,8 @@ actions!(
         FoldRecursive,
         /// Folds the selected ranges.
         FoldSelectedRanges,
+        /// Toggles focus back to the last active buffer.
+        ToggleFocus,
         /// Toggles folding at the current position.
         ToggleFold,
         /// Toggles recursive folding at the current position.

crates/editor/src/editor.rs 🔗

@@ -356,6 +356,7 @@ pub fn init(cx: &mut App) {
             workspace.register_action(Editor::new_file_vertical);
             workspace.register_action(Editor::new_file_horizontal);
             workspace.register_action(Editor::cancel_language_server_work);
+            workspace.register_action(Editor::toggle_focus);
         },
     )
     .detach();
@@ -16954,6 +16955,18 @@ impl Editor {
         cx.notify();
     }
 
+    pub fn toggle_focus(
+        workspace: &mut Workspace,
+        _: &actions::ToggleFocus,
+        window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) {
+        let Some(item) = workspace.recent_active_item_by_type::<Self>(cx) else {
+            return;
+        };
+        workspace.activate_item(&item, true, true, window, cx);
+    }
+
     pub fn toggle_fold(
         &mut self,
         _: &actions::ToggleFold,

crates/workspace/src/workspace.rs 🔗

@@ -1711,6 +1711,27 @@ impl Workspace {
         history
     }
 
+    pub fn recent_active_item_by_type<T: 'static>(&self, cx: &App) -> Option<Entity<T>> {
+        let mut recent_item: Option<Entity<T>> = None;
+        let mut recent_timestamp = 0;
+        for pane_handle in &self.panes {
+            let pane = pane_handle.read(cx);
+            let item_map: HashMap<EntityId, &Box<dyn ItemHandle>> =
+                pane.items().map(|item| (item.item_id(), item)).collect();
+            for entry in pane.activation_history() {
+                if entry.timestamp > recent_timestamp {
+                    if let Some(&item) = item_map.get(&entry.entity_id) {
+                        if let Some(typed_item) = item.act_as::<T>(cx) {
+                            recent_timestamp = entry.timestamp;
+                            recent_item = Some(typed_item);
+                        }
+                    }
+                }
+            }
+        }
+        recent_item
+    }
+
     pub fn recent_navigation_history_iter(
         &self,
         cx: &App,