diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 984cf953f66cde..58252ba05487d5 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -1385,10 +1385,22 @@ impl PickerDelegate for FileFinderDelegate { let context = context.clone(); move |menu, _, _| { menu.context(context) - .action("Split Left", pane::SplitLeft.boxed_clone()) - .action("Split Right", pane::SplitRight.boxed_clone()) - .action("Split Up", pane::SplitUp.boxed_clone()) - .action("Split Down", pane::SplitDown.boxed_clone()) + .action( + "Split Left", + pane::SplitLeft::default().boxed_clone(), + ) + .action( + "Split Right", + pane::SplitRight::default().boxed_clone(), + ) + .action( + "Split Up", + pane::SplitUp::default().boxed_clone(), + ) + .action( + "Split Down", + pane::SplitDown::default().boxed_clone(), + ) } })) } diff --git a/crates/language_tools/src/key_context_view.rs b/crates/language_tools/src/key_context_view.rs index 88a68956d351b4..9d9ed15f3ab181 100644 --- a/crates/language_tools/src/key_context_view.rs +++ b/crates/language_tools/src/key_context_view.rs @@ -219,7 +219,7 @@ impl Render for KeyContextView { cx )) .on_click(|_, window, cx| { - window.dispatch_action(workspace::SplitRight.boxed_clone(), cx); + window.dispatch_action(workspace::SplitRight::default().boxed_clone(), cx); window.dispatch_action(zed_actions::OpenDefaultKeymap.boxed_clone(), cx); }), ) @@ -228,7 +228,7 @@ impl Render for KeyContextView { .style(ButtonStyle::Filled) .key_binding(ui::KeyBinding::for_action(&zed_actions::OpenKeymap, window, cx)) .on_click(|_, window, cx| { - window.dispatch_action(workspace::SplitRight.boxed_clone(), cx); + window.dispatch_action(workspace::SplitRight::default().boxed_clone(), cx); window.dispatch_action(zed_actions::OpenKeymap.boxed_clone(), cx); }), ), diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 4237d27349aee3..4bd395cd5b1989 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -183,10 +183,10 @@ impl TerminalPanel { split_context.clone(), |menu, split_context| menu.context(split_context), ) - .action("Split Right", SplitRight.boxed_clone()) - .action("Split Left", SplitLeft.boxed_clone()) - .action("Split Up", SplitUp.boxed_clone()) - .action("Split Down", SplitDown.boxed_clone()) + .action("Split Right", SplitRight::default().boxed_clone()) + .action("Split Left", SplitLeft::default().boxed_clone()) + .action("Split Up", SplitUp::default().boxed_clone()) + .action("Split Down", SplitDown::default().boxed_clone()) }) .into() } @@ -390,7 +390,8 @@ impl TerminalPanel { } self.serialize(cx); } - pane::Event::Split(direction) => { + pane::Event::Split { direction, .. } => { + // TODO: support Split::use_existing let Some(new_pane) = self.new_pane_with_cloned_active_terminal(window, cx) else { return; }; diff --git a/crates/vim/src/command.rs b/crates/vim/src/command.rs index 12b7211b8e6c8a..7819dacf84c313 100644 --- a/crates/vim/src/command.rs +++ b/crates/vim/src/command.rs @@ -770,8 +770,8 @@ fn generate_commands(_: &App) -> Vec { save_intent: Some(SaveIntent::Overwrite), }), VimCommand::new(("cq", "uit"), zed_actions::Quit), - VimCommand::new(("sp", "lit"), workspace::SplitHorizontal), - VimCommand::new(("vs", "plit"), workspace::SplitVertical), + VimCommand::new(("sp", "lit"), workspace::SplitHorizontal::default()), + VimCommand::new(("vs", "plit"), workspace::SplitVertical::default()), VimCommand::new( ("bd", "elete"), workspace::CloseActiveItem { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 18069c4a96b3b3..be7488fb2936da 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -154,6 +154,48 @@ pub struct DeploySearch { pub replace_enabled: bool, } +#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] +#[serde(deny_unknown_fields)] +pub struct SplitRight { + #[serde(default)] + pub use_existing: bool, +} + +#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] +#[serde(deny_unknown_fields)] +pub struct SplitLeft { + #[serde(default)] + pub use_existing: bool, +} + +#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] +#[serde(deny_unknown_fields)] +pub struct SplitUp { + #[serde(default)] + pub use_existing: bool, +} + +#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] +#[serde(deny_unknown_fields)] +pub struct SplitDown { + #[serde(default)] + pub use_existing: bool, +} + +#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] +#[serde(deny_unknown_fields)] +pub struct SplitHorizontal { + #[serde(default)] + pub use_existing: bool, +} + +#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] +#[serde(deny_unknown_fields)] +pub struct SplitVertical { + #[serde(default)] + pub use_existing: bool, +} + impl_actions!( pane, [ @@ -166,6 +208,12 @@ impl_actions!( ActivateItem, RevealInProjectPanel, DeploySearch, + SplitRight, + SplitLeft, + SplitUp, + SplitDown, + SplitHorizontal, + SplitVertical, ] ); @@ -181,12 +229,6 @@ actions!( JoinIntoNext, JoinAll, ReopenClosedItem, - SplitLeft, - SplitUp, - SplitRight, - SplitDown, - SplitHorizontal, - SplitVertical, SwapItemLeft, SwapItemRight, TogglePreviewTab, @@ -221,7 +263,10 @@ pub enum Event { RemovedItem { item_id: EntityId, }, - Split(SplitDirection), + Split { + direction: SplitDirection, + use_existing: bool, + }, JoinAll, JoinIntoNext, ChangeItemTitle, @@ -251,9 +296,13 @@ impl fmt::Debug for Event { .debug_struct("RemovedItem") .field("item_id", item_id) .finish(), - Event::Split(direction) => f + Event::Split { + direction, + use_existing, + } => f .debug_struct("Split") .field("direction", direction) + .field("use_existing", use_existing) .finish(), Event::JoinAll => f.write_str("JoinAll"), Event::JoinIntoNext => f.write_str("JoinIntoNext"), @@ -482,10 +531,10 @@ impl Pane { .with_handle(pane.split_item_context_menu_handle.clone()) .menu(move |window, cx| { ContextMenu::build(window, cx, |menu, _, _| { - menu.action("Split Right", SplitRight.boxed_clone()) - .action("Split Left", SplitLeft.boxed_clone()) - .action("Split Up", SplitUp.boxed_clone()) - .action("Split Down", SplitDown.boxed_clone()) + menu.action("Split Right", SplitRight::default().boxed_clone()) + .action("Split Left", SplitLeft::default().boxed_clone()) + .action("Split Up", SplitUp::default().boxed_clone()) + .action("Split Down", SplitDown::default().boxed_clone()) }) .into() }), @@ -2004,8 +2053,11 @@ impl Pane { } } - pub fn split(&mut self, direction: SplitDirection, cx: &mut Context) { - cx.emit(Event::Split(direction)); + pub fn split(&mut self, direction: SplitDirection, use_existing: bool, cx: &mut Context) { + cx.emit(Event::Split { + direction, + use_existing, + }); } pub fn toolbar(&self) -> &Entity { @@ -3108,22 +3160,24 @@ impl Render for Pane { .on_action(cx.listener(|pane, _: &AlternateFile, window, cx| { pane.alternate_file(window, cx); })) - .on_action( - cx.listener(|pane, _: &SplitLeft, _, cx| pane.split(SplitDirection::Left, cx)), - ) - .on_action(cx.listener(|pane, _: &SplitUp, _, cx| pane.split(SplitDirection::Up, cx))) - .on_action(cx.listener(|pane, _: &SplitHorizontal, _, cx| { - pane.split(SplitDirection::horizontal(cx), cx) + .on_action(cx.listener(|pane, action: &SplitLeft, _, cx| { + pane.split(SplitDirection::Left, action.use_existing, cx) })) - .on_action(cx.listener(|pane, _: &SplitVertical, _, cx| { - pane.split(SplitDirection::vertical(cx), cx) + .on_action(cx.listener(|pane, action: &SplitUp, _, cx| { + pane.split(SplitDirection::Up, action.use_existing, cx) + })) + .on_action(cx.listener(|pane, action: &SplitHorizontal, _, cx| { + pane.split(SplitDirection::horizontal(cx), action.use_existing, cx) + })) + .on_action(cx.listener(|pane, action: &SplitVertical, _, cx| { + pane.split(SplitDirection::vertical(cx), action.use_existing, cx) + })) + .on_action(cx.listener(|pane, action: &SplitRight, _, cx| { + pane.split(SplitDirection::Right, action.use_existing, cx) + })) + .on_action(cx.listener(|pane, action: &SplitDown, _, cx| { + pane.split(SplitDirection::Down, action.use_existing, cx) })) - .on_action( - cx.listener(|pane, _: &SplitRight, _, cx| pane.split(SplitDirection::Right, cx)), - ) - .on_action( - cx.listener(|pane, _: &SplitDown, _, cx| pane.split(SplitDirection::Down, cx)), - ) .on_action( cx.listener(|pane, _: &GoBack, window, cx| pane.navigate_backward(window, cx)), ) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 126e1306077b2b..03a9fb50118e68 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3236,6 +3236,27 @@ impl Workspace { } } + pub fn find_or_create_pane_in_direction( + &mut self, + window: &mut Window, + pane: &Entity, + direction: SplitDirection, + cx: &mut Context, + ) -> Entity { + if let Some(target_pane) = self + .center + .find_pane_in_direction(&pane, direction, cx) + .cloned() + { + target_pane + } else { + let new_pane = self.add_pane(window, cx); + self.center.split(pane, &new_pane, direction).unwrap(); + cx.notify(); + new_pane + } + } + pub fn resize_pane( &mut self, axis: gpui::Axis, @@ -3334,8 +3355,15 @@ impl Workspace { item: item.boxed_clone(), }); } - pane::Event::Split(direction) => { - self.split_and_clone(pane.clone(), *direction, window, cx); + pane::Event::Split { + direction, + use_existing, + } => { + if *use_existing { + self.clone_into_split_or_existing_pane(pane.clone(), *direction, window, cx); + } else { + self.split_and_clone(pane.clone(), *direction, window, cx); + } } pane::Event::JoinIntoNext => { self.join_pane_into_next(pane.clone(), window, cx); @@ -3472,6 +3500,40 @@ impl Workspace { maybe_pane_handle } + /// Clone the active item in `pane` and move the clone into an existing pane + /// in the designated direction or a split of the current pane in that direction. + pub fn clone_into_split_or_existing_pane( + &mut self, + pane: Entity, + direction: SplitDirection, + window: &mut Window, + cx: &mut Context, + ) -> Option> { + let item = pane.read(cx).active_item()?; + let maybe_pane_handle = if let Some(clone) = + item.clone_on_split(self.database_id(), window, cx) + { + let target_pane = self.find_or_create_pane_in_direction(window, &pane, direction, cx); + target_pane.update(cx, |pane, cx| { + let project_entry_id = clone + .is_singleton(cx) + .then_some(Some(())) + .and_then(|_| clone.project_entry_ids(cx).first().cloned()); + if let Some(project_entry_id) = project_entry_id { + if let Some(existing) = pane.item_for_entry(project_entry_id, cx) { + pane.remove_item(existing.item_id(), true, false, window, cx); + } + } + pane.add_item(clone, true, true, None, window, cx); + }); + Some(target_pane) + } else { + None + }; + cx.notify(); + maybe_pane_handle + } + pub fn split_pane_with_item( &mut self, pane_to_split: WeakEntity, diff --git a/crates/zed/src/zed/app_menus.rs b/crates/zed/src/zed/app_menus.rs index ec5dc29ee8ddb9..0c3650781ffffe 100644 --- a/crates/zed/src/zed/app_menus.rs +++ b/crates/zed/src/zed/app_menus.rs @@ -151,10 +151,10 @@ pub fn app_menus() -> Vec { MenuItem::submenu(Menu { name: "Editor Layout".into(), items: vec![ - MenuItem::action("Split Up", workspace::SplitUp), - MenuItem::action("Split Down", workspace::SplitDown), - MenuItem::action("Split Left", workspace::SplitLeft), - MenuItem::action("Split Right", workspace::SplitRight), + MenuItem::action("Split Up", workspace::SplitUp::default()), + MenuItem::action("Split Down", workspace::SplitDown::default()), + MenuItem::action("Split Left", workspace::SplitLeft::default()), + MenuItem::action("Split Right", workspace::SplitRight::default()), ], }), MenuItem::separator(), diff --git a/script/bundle-linux b/script/bundle-linux index e40eb33c93a8ff..9ea492a0f32053 100755 --- a/script/bundle-linux +++ b/script/bundle-linux @@ -39,7 +39,6 @@ version_info=$(rustc --version --verbose) host_line=$(echo "$version_info" | grep host) target_triple=${host_line#*: } musl_triple=${target_triple%-gnu}-musl -remote_server_triple=${REMOTE_SERVER_TARGET:-"${musl_triple}"} rustup_installed=false if command -v rustup >/dev/null 2>&1; then rustup_installed=true @@ -48,47 +47,26 @@ fi # Generate the licenses first, so they can be baked into the binaries script/generate-licenses -if "$rustup_installed"; then - rustup target add "$remote_server_triple" -fi - export CC=$(which clang) # Build binary in release mode export RUSTFLAGS="${RUSTFLAGS:-} -C link-args=-Wl,--disable-new-dtags,-rpath,\$ORIGIN/../lib" cargo build --release --target "${target_triple}" --package zed --package cli -# Build remote_server in separate invocation to prevent feature unification from other crates -# from influencing dynamic libraries required by it. -if [[ "$remote_server_triple" == "$musl_triple" ]]; then - export RUSTFLAGS="${RUSTFLAGS:-} -C target-feature=+crt-static" -fi -cargo build --release --target "${remote_server_triple}" --package remote_server # Strip debug symbols and save them for upload to DigitalOcean objcopy --only-keep-debug "${target_dir}/${target_triple}/release/zed" "${target_dir}/${target_triple}/release/zed.dbg" -objcopy --only-keep-debug "${target_dir}/${remote_server_triple}/release/remote_server" "${target_dir}/${remote_server_triple}/release/remote_server.dbg" objcopy --strip-debug "${target_dir}/${target_triple}/release/zed" objcopy --strip-debug "${target_dir}/${target_triple}/release/cli" -objcopy --strip-debug "${target_dir}/${remote_server_triple}/release/remote_server" gzip -f "${target_dir}/${target_triple}/release/zed.dbg" -gzip -f "${target_dir}/${remote_server_triple}/release/remote_server.dbg" if [[ -n "${DIGITALOCEAN_SPACES_SECRET_KEY:-}" && -n "${DIGITALOCEAN_SPACES_ACCESS_KEY:-}" ]]; then upload_to_blob_store_public \ "zed-debug-symbols" \ "${target_dir}/${target_triple}/release/zed.dbg.gz" \ "$channel/zed-$version-${target_triple}.dbg.gz" - upload_to_blob_store_public \ - "zed-debug-symbols" \ - "${target_dir}/${remote_server_triple}/release/remote_server.dbg.gz" \ - "$channel/remote_server-$version-${remote_server_triple}.dbg.gz" fi -# Ensure that remote_server does not depend on libssl nor libcrypto, as we got rid of these deps. -if ldd "${target_dir}/${remote_server_triple}/release/remote_server" | grep -q 'libcrypto\|libssl'; then - echo "Error: remote_server still depends on libssl or libcrypto" && exit 1 -fi suffix="" if [ "$channel" != "stable" ]; then @@ -157,4 +135,3 @@ remove_match="zed(-[a-zA-Z0-9]+)?-linux-$(uname -m)\.tar\.gz" ls "${target_dir}/release" | grep -E ${remove_match} | xargs -d "\n" -I {} rm -f "${target_dir}/release/{}" || true tar -czvf "${target_dir}/release/$archive" -C ${temp_dir} "zed$suffix.app" -gzip -f --stdout --best "${target_dir}/${remote_server_triple}/release/remote_server" > "${target_dir}/zed-remote-server-linux-${arch}.gz"