diff --git a/assets/keymaps/linux/sublime_text.json b/assets/keymaps/linux/sublime_text.json index e4454d69bbcdfd..0a5a22c0728c24 100644 --- a/assets/keymaps/linux/sublime_text.json +++ b/assets/keymaps/linux/sublime_text.json @@ -4,12 +4,32 @@ "ctrl-shift-[": "pane::ActivatePrevItem", "ctrl-shift-]": "pane::ActivateNextItem", "ctrl-pageup": "pane::ActivatePrevItem", - "ctrl-pagedown": "pane::ActivateNextItem" + "ctrl-pagedown": "pane::ActivateNextItem", + "ctrl-1": ["workspace::ActivatePane", 0], + "ctrl-2": ["workspace::ActivatePane", 1], + "ctrl-3": ["workspace::ActivatePane", 2], + "ctrl-4": ["workspace::ActivatePane", 3], + "ctrl-5": ["workspace::ActivatePane", 4], + "ctrl-6": ["workspace::ActivatePane", 5], + "ctrl-7": ["workspace::ActivatePane", 6], + "ctrl-8": ["workspace::ActivatePane", 7], + "ctrl-9": ["workspace::ActivatePane", 8], + "ctrl-shift-1": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }], + "ctrl-shift-2": ["workspace::MoveItemToPane", { "destination": 1 }], + "ctrl-shift-3": ["workspace::MoveItemToPane", { "destination": 2 }], + "ctrl-shift-4": ["workspace::MoveItemToPane", { "destination": 3 }], + "ctrl-shift-5": ["workspace::MoveItemToPane", { "destination": 4 }], + "ctrl-shift-6": ["workspace::MoveItemToPane", { "destination": 5 }], + "ctrl-shift-7": ["workspace::MoveItemToPane", { "destination": 6 }], + "ctrl-shift-8": ["workspace::MoveItemToPane", { "destination": 7 }], + "ctrl-shift-9": ["workspace::MoveItemToPane", { "destination": 8 }] } }, { "context": "Editor", "bindings": { + "ctrl-alt-up": "editor::AddSelectionAbove", + "ctrl-alt-down": "editor::AddSelectionBelow", "ctrl-shift-up": "editor::MoveLineUp", "ctrl-shift-down": "editor::MoveLineDown", "ctrl-shift-m": "editor::SelectLargerSyntaxNode", @@ -17,6 +37,8 @@ "ctrl-shift-a": "editor::SelectLargerSyntaxNode", "ctrl-shift-d": "editor::DuplicateSelection", "alt-f3": "editor::SelectAllMatches", // find_all_under + "f9": "editor::SortLinesCaseSensitive", + "ctrl-f9": "editor::SortLinesCaseInsensitive", "f12": "editor::GoToDefinition", "ctrl-f12": "editor::GoToDefinitionSplit", "shift-f12": "editor::FindAllReferences", diff --git a/assets/keymaps/macos/sublime_text.json b/assets/keymaps/macos/sublime_text.json index 120dbf41282821..77162a7cc7e89c 100644 --- a/assets/keymaps/macos/sublime_text.json +++ b/assets/keymaps/macos/sublime_text.json @@ -4,7 +4,25 @@ "cmd-shift-[": "pane::ActivatePrevItem", "cmd-shift-]": "pane::ActivateNextItem", "ctrl-pageup": "pane::ActivatePrevItem", - "ctrl-pagedown": "pane::ActivateNextItem" + "ctrl-pagedown": "pane::ActivateNextItem", + "ctrl-1": ["workspace::ActivatePane", 0], + "ctrl-2": ["workspace::ActivatePane", 1], + "ctrl-3": ["workspace::ActivatePane", 2], + "ctrl-4": ["workspace::ActivatePane", 3], + "ctrl-5": ["workspace::ActivatePane", 4], + "ctrl-6": ["workspace::ActivatePane", 5], + "ctrl-7": ["workspace::ActivatePane", 6], + "ctrl-8": ["workspace::ActivatePane", 7], + "ctrl-9": ["workspace::ActivatePane", 8], + "ctrl-shift-1": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }], + "ctrl-shift-2": ["workspace::MoveItemToPane", { "destination": 1 }], + "ctrl-shift-3": ["workspace::MoveItemToPane", { "destination": 2 }], + "ctrl-shift-4": ["workspace::MoveItemToPane", { "destination": 3 }], + "ctrl-shift-5": ["workspace::MoveItemToPane", { "destination": 4 }], + "ctrl-shift-6": ["workspace::MoveItemToPane", { "destination": 5 }], + "ctrl-shift-7": ["workspace::MoveItemToPane", { "destination": 6 }], + "ctrl-shift-8": ["workspace::MoveItemToPane", { "destination": 7 }], + "ctrl-shift-9": ["workspace::MoveItemToPane", { "destination": 8 }] } }, { @@ -20,6 +38,8 @@ "cmd-shift-a": "editor::SelectLargerSyntaxNode", "cmd-shift-d": "editor::DuplicateSelection", "ctrl-cmd-g": "editor::SelectAllMatches", // find_all_under + "f5": "editor::SortLinesCaseSensitive", + "ctrl-f5": "editor::SortLinesCaseInsensitive", "shift-f12": "editor::FindAllReferences", "alt-cmd-down": "editor::GoToDefinition", "ctrl-alt-cmd-down": "editor::GoToDefinitionSplit", diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 04e2c8fdd49aa6..a3cff1dbd41501 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -33,11 +33,12 @@ use util::{ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel, PanelEvent}, item::SerializableItem, - move_item, pane, + move_active_item, move_item, pane, ui::IconName, ActivateNextPane, ActivatePane, ActivatePaneInDirection, ActivatePreviousPane, DraggedTab, - ItemId, NewTerminal, Pane, PaneGroup, SplitDirection, SplitDown, SplitLeft, SplitRight, - SplitUp, SwapPaneInDirection, ToggleZoom, Workspace, + ItemId, MoveItemToPane, MoveItemToPaneInDirection, NewTerminal, Pane, PaneGroup, + SplitDirection, SplitDown, SplitLeft, SplitRight, SplitUp, SwapPaneInDirection, ToggleZoom, + Workspace, }; use anyhow::{anyhow, Context, Result}; @@ -355,7 +356,7 @@ impl TerminalPanel { &mut self, cx: &mut ViewContext, ) -> Option> { - let workspace = self.workspace.clone().upgrade()?; + let workspace = self.workspace.upgrade()?; let workspace = workspace.read(cx); let database_id = workspace.database_id(); let weak_workspace = self.workspace.clone(); @@ -1181,8 +1182,7 @@ impl Render for TerminalPanel { .position(|pane| **pane == terminal_panel.active_pane) { let next_ix = (ix + 1) % panes.len(); - let next_pane = panes[next_ix].clone(); - cx.focus_view(&next_pane); + cx.focus_view(&panes[next_ix]); } }), ) @@ -1194,15 +1194,14 @@ impl Render for TerminalPanel { .position(|pane| **pane == terminal_panel.active_pane) { let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1); - let prev_pane = panes[prev_ix].clone(); - cx.focus_view(&prev_pane); + cx.focus_view(&panes[prev_ix]); } }), ) .on_action(cx.listener(|terminal_panel, action: &ActivatePane, cx| { let panes = terminal_panel.center.panes(); - if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) { - cx.focus_view(&pane); + if let Some(&pane) = panes.get(action.0) { + cx.focus_view(pane); } else { if let Some(new_pane) = terminal_panel.new_pane_with_cloned_active_terminal(cx) @@ -1219,18 +1218,40 @@ impl Render for TerminalPanel { } } })) - .on_action(cx.listener( - |terminal_panel, action: &SwapPaneInDirection, cx| { + .on_action( + cx.listener(|terminal_panel, action: &SwapPaneInDirection, cx| { if let Some(to) = terminal_panel .center .find_pane_in_direction(&terminal_panel.active_pane, action.0, cx) .cloned() { - terminal_panel - .center - .swap(&terminal_panel.active_pane.clone(), &to); + terminal_panel.center.swap(&terminal_panel.active_pane, &to); cx.notify(); } + }), + ) + .on_action(cx.listener(|terminal_panel, action: &MoveItemToPane, cx| { + let Some(&target_pane) = terminal_panel.center.panes().get(action.destination) + else { + return; + }; + move_active_item( + &terminal_panel.active_pane, + target_pane, + action.focus, + true, + cx, + ); + })) + .on_action(cx.listener( + |terminal_panel, action: &MoveItemToPaneInDirection, cx| { + let source_pane = &terminal_panel.active_pane; + if let Some(destination_pane) = terminal_panel + .center + .find_pane_in_direction(source_pane, action.direction, cx) + { + move_active_item(source_pane, destination_pane, action.focus, true, cx); + }; }, )) }) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 517c9a86da4971..2a9c05b737fc50 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -93,7 +93,7 @@ use theme::{ActiveTheme, SystemAppearance, ThemeSettings}; pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView}; pub use ui; use ui::prelude::*; -use util::{paths::SanitizedPath, ResultExt, TryFutureExt}; +use util::{paths::SanitizedPath, serde::default_true, ResultExt, TryFutureExt}; use uuid::Uuid; pub use workspace_settings::{ AutosaveSetting, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings, @@ -173,6 +173,20 @@ pub struct ActivatePaneInDirection(pub SplitDirection); #[derive(Clone, Deserialize, PartialEq)] pub struct SwapPaneInDirection(pub SplitDirection); +#[derive(Clone, Deserialize, PartialEq)] +pub struct MoveItemToPane { + pub destination: usize, + #[serde(default = "default_true")] + pub focus: bool, +} + +#[derive(Clone, Deserialize, PartialEq)] +pub struct MoveItemToPaneInDirection { + pub direction: SplitDirection, + #[serde(default = "default_true")] + pub focus: bool, +} + #[derive(Clone, PartialEq, Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SaveAll { @@ -222,6 +236,8 @@ impl_actions!( ActivatePaneInDirection, CloseAllItemsAndPanes, CloseInactiveTabsAndPanes, + MoveItemToPane, + MoveItemToPaneInDirection, OpenTerminal, Reload, Save, @@ -2829,6 +2845,13 @@ impl Workspace { } } + fn move_item_to_pane_at_index(&mut self, action: &MoveItemToPane, cx: &mut ViewContext) { + let Some(&target_pane) = self.center.panes().get(action.destination) else { + return; + }; + move_active_item(&self.active_pane, target_pane, action.focus, true, cx); + } + pub fn activate_next_pane(&mut self, cx: &mut WindowContext) { let panes = self.center.panes(); if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) { @@ -2947,6 +2970,16 @@ impl Workspace { } } + pub fn move_item_to_pane_in_direction( + &mut self, + action: &MoveItemToPaneInDirection, + cx: &mut WindowContext, + ) { + if let Some(destination) = self.find_pane_in_direction(action.direction, cx) { + move_active_item(&self.active_pane, &destination, action.focus, true, cx); + } + } + pub fn bounding_box_for_pane(&self, pane: &View) -> Option> { self.center.bounding_box_for_pane(pane) } @@ -2967,14 +3000,14 @@ impl Workspace { cx: &mut ViewContext, ) { if let Some(to) = self.find_pane_in_direction(direction, cx) { - self.center.swap(&self.active_pane.clone(), &to); + self.center.swap(&self.active_pane, &to); cx.notify(); } } pub fn resize_pane(&mut self, axis: gpui::Axis, amount: Pixels, cx: &mut ViewContext) { self.center - .resize(&self.active_pane.clone(), axis, amount, &self.bounds); + .resize(&self.active_pane, axis, amount, &self.bounds); cx.notify(); } @@ -4408,6 +4441,7 @@ impl Workspace { .on_action(cx.listener(Self::follow_next_collaborator)) .on_action(cx.listener(Self::close_window)) .on_action(cx.listener(Self::activate_pane_at_index)) + .on_action(cx.listener(Self::move_item_to_pane_at_index)) .on_action(cx.listener(|workspace, _: &Unfollow, cx| { let pane = workspace.active_pane().clone(); workspace.unfollow_in_pane(&pane, cx); @@ -4438,6 +4472,11 @@ impl Workspace { workspace.activate_pane_in_direction(action.0, cx) }), ) + .on_action( + cx.listener(|workspace, action: &MoveItemToPaneInDirection, cx| { + workspace.move_item_to_pane_in_direction(action, cx) + }), + ) .on_action(cx.listener(|workspace, action: &SwapPaneInDirection, cx| { workspace.swap_pane_in_direction(action.0, cx) })) @@ -6181,6 +6220,34 @@ pub fn move_item( }); } +pub fn move_active_item( + source: &View, + destination: &View, + focus_destination: bool, + close_if_empty: bool, + cx: &mut WindowContext<'_>, +) { + if source == destination { + return; + } + let Some(active_item) = source.read(cx).active_item() else { + return; + }; + source.update(cx, |source_pane, cx| { + let item_id = active_item.item_id(); + source_pane.remove_item(item_id, false, close_if_empty, cx); + destination.update(cx, |target_pane, cx| { + target_pane.add_item( + active_item, + focus_destination, + focus_destination, + Some(target_pane.items_len()), + cx, + ); + }); + }); +} + #[cfg(test)] mod tests { use std::{cell::RefCell, rc::Rc};