From c7669317ec6c345ef5e6cbd93636d2fcb1fa4650 Mon Sep 17 00:00:00 2001 From: Alex Viscreanu Date: Fri, 14 Jul 2023 21:41:23 +0200 Subject: [PATCH 1/2] feat(workspace): allow alternative actions to open files and symbols in split Co-authored-by: Mikayla Maki --- assets/keymaps/atom.json | 1 + assets/keymaps/default.json | 3 + assets/keymaps/jetbrains.json | 3 +- assets/keymaps/sublime_text.json | 1 + assets/keymaps/textmate.json | 1 + crates/collab/src/tests/integration_tests.rs | 6 +- crates/collab_ui/src/contact_finder.rs | 2 +- crates/command_palette/src/command_palette.rs | 2 +- crates/editor/src/editor.rs | 49 ++++++++-- crates/editor/src/element.rs | 6 +- crates/editor/src/link_go_to_definition.rs | 15 +-- crates/file_finder/src/file_finder.rs | 90 ++++++++++------- .../src/language_selector.rs | 2 +- crates/menu/src/menu.rs | 1 + crates/outline/src/outline.rs | 2 +- crates/picker/src/picker.rs | 20 ++-- crates/project_panel/src/project_panel.rs | 28 +++++- crates/project_symbols/src/project_symbols.rs | 9 +- crates/recent_projects/src/recent_projects.rs | 2 +- crates/theme_selector/src/theme_selector.rs | 2 +- crates/vcs_menu/src/lib.rs | 2 +- crates/vector_store/src/modal.rs | 2 +- crates/welcome/src/base_keymap_picker.rs | 2 +- crates/workspace/src/workspace.rs | 98 ++++++++++++++++++- crates/zed/src/zed.rs | 8 +- 25 files changed, 277 insertions(+), 80 deletions(-) diff --git a/assets/keymaps/atom.json b/assets/keymaps/atom.json index af845ae4f2111c6009a6b0629777987b22f1b8e3..c2beb71b56948d8e65c804baeb1dc8a71851ee0f 100644 --- a/assets/keymaps/atom.json +++ b/assets/keymaps/atom.json @@ -9,6 +9,7 @@ "context": "Editor", "bindings": { "cmd-b": "editor::GoToDefinition", + "alt-cmd-b": "editor::GoToDefinitionSplit", "cmd-<": "editor::ScrollCursorCenter", "cmd-g": [ "editor::SelectNext", diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index c044f4b6004f85a43218f4944dff6d4959fb9f96..1a13d8cdb30ce4192d76179fa86dba5812539c95 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -13,6 +13,7 @@ "cmd-up": "menu::SelectFirst", "cmd-down": "menu::SelectLast", "enter": "menu::Confirm", + "cmd-enter": "menu::SecondaryConfirm", "escape": "menu::Cancel", "ctrl-c": "menu::Cancel", "cmd-{": "pane::ActivatePrevItem", @@ -298,7 +299,9 @@ "shift-f8": "editor::GoToPrevDiagnostic", "f2": "editor::Rename", "f12": "editor::GoToDefinition", + "alt-f12": "editor::GoToDefinitionSplit", "cmd-f12": "editor::GoToTypeDefinition", + "alt-cmd-f12": "editor::GoToTypeDefinitionSplit", "alt-shift-f12": "editor::FindAllReferences", "ctrl-m": "editor::MoveToEnclosingBracket", "alt-cmd-[": "editor::Fold", diff --git a/assets/keymaps/jetbrains.json b/assets/keymaps/jetbrains.json index b3e8f989a4a0337a3c1dd9dff63ef640d64dc6ef..ab093a8deb49059659addb9255564d8a7106412f 100644 --- a/assets/keymaps/jetbrains.json +++ b/assets/keymaps/jetbrains.json @@ -46,8 +46,9 @@ "alt-f7": "editor::FindAllReferences", "cmd-alt-f7": "editor::FindAllReferences", "cmd-b": "editor::GoToDefinition", - "cmd-alt-b": "editor::GoToDefinition", + "cmd-alt-b": "editor::GoToDefinitionSplit", "cmd-shift-b": "editor::GoToTypeDefinition", + "cmd-alt-shift-b": "editor::GoToTypeDefinitionSplit", "alt-enter": "editor::ToggleCodeActions", "f2": "editor::GoToDiagnostic", "cmd-f2": "editor::GoToPrevDiagnostic", diff --git a/assets/keymaps/sublime_text.json b/assets/keymaps/sublime_text.json index ca20802295923ec992ceaeb6dc2f514aa6a628c9..a70a61af5519f5ecb9ea161d650ccb96f4b99909 100644 --- a/assets/keymaps/sublime_text.json +++ b/assets/keymaps/sublime_text.json @@ -20,6 +20,7 @@ "cmd-shift-a": "editor::SelectLargerSyntaxNode", "shift-f12": "editor::FindAllReferences", "alt-cmd-down": "editor::GoToDefinition", + "ctrl-alt-cmd-down": "editor::GoToDefinitionSplit", "alt-shift-cmd-down": "editor::FindAllReferences", "ctrl-.": "editor::GoToHunk", "ctrl-,": "editor::GoToPrevHunk", diff --git a/assets/keymaps/textmate.json b/assets/keymaps/textmate.json index 1f28c05158a9bee72cb697368d8bc114cec1b046..90eb090211a753c12c64d2fbd2782681e0e36ae6 100644 --- a/assets/keymaps/textmate.json +++ b/assets/keymaps/textmate.json @@ -12,6 +12,7 @@ "cmd-l": "go_to_line::Toggle", "ctrl-shift-d": "editor::DuplicateLine", "cmd-b": "editor::GoToDefinition", + "alt-cmd-b": "editor::GoToDefinition", "cmd-j": "editor::ScrollCursorCenter", "cmd-shift-l": "editor::SelectLine", "cmd-shift-t": "outline::Toggle", diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index c32129818fdd7f7c5274b6e0fdddbd16da4b8c02..ab94f16a07f011b8166f7b7ff779539b89401034 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -7217,7 +7217,7 @@ async fn test_peers_following_each_other( // Clients A and B follow each other in split panes workspace_a.update(cx_a, |workspace, cx| { - workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx); + workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx); }); workspace_a .update(cx_a, |workspace, cx| { @@ -7228,7 +7228,7 @@ async fn test_peers_following_each_other( .await .unwrap(); workspace_b.update(cx_b, |workspace, cx| { - workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx); + workspace.split_and_clone(workspace.active_pane().clone(), SplitDirection::Right, cx); }); workspace_b .update(cx_b, |workspace, cx| { @@ -7455,7 +7455,7 @@ async fn test_auto_unfollowing( // When client B activates a different pane, it continues following client A in the original pane. workspace_b.update(cx_b, |workspace, cx| { - workspace.split_pane(pane_b.clone(), SplitDirection::Right, cx) + workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, cx) }); assert_eq!( workspace_b.read_with(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)), diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index af59817ece40ab0fc3c15adb35c78b4d8aa84898..3264a144ed4bc794b2bf0a052fd862c8534d18fd 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -67,7 +67,7 @@ impl PickerDelegate for ContactFinderDelegate { }) } - fn confirm(&mut self, cx: &mut ViewContext>) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext>) { if let Some(user) = self.potential_contacts.get(self.selected_index) { let user_store = self.user_store.read(cx); match user_store.contact_request_status(user) { diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 77dde09875910fdad88abd1a8ca04dc25e412f86..7461fb28c7382bfaaf9f97385579e161251276fc 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -160,7 +160,7 @@ impl PickerDelegate for CommandPaletteDelegate { fn dismissed(&mut self, _cx: &mut ViewContext>) {} - fn confirm(&mut self, cx: &mut ViewContext>) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext>) { if !self.matches.is_empty() { let window_id = cx.window_id(); let focused_view_id = self.focused_view_id; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 388f1aae88d86727efe7d02dae58da537b8c411e..b8a7853a93e8f35aeab7016b7e61c4fe629ea4c6 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -271,7 +271,9 @@ actions!( SelectLargerSyntaxNode, SelectSmallerSyntaxNode, GoToDefinition, + GoToDefinitionSplit, GoToTypeDefinition, + GoToTypeDefinitionSplit, MoveToEnclosingBracket, UndoSelection, RedoSelection, @@ -407,7 +409,9 @@ pub fn init(cx: &mut AppContext) { cx.add_action(Editor::go_to_hunk); cx.add_action(Editor::go_to_prev_hunk); cx.add_action(Editor::go_to_definition); + cx.add_action(Editor::go_to_definition_split); cx.add_action(Editor::go_to_type_definition); + cx.add_action(Editor::go_to_type_definition_split); cx.add_action(Editor::fold); cx.add_action(Editor::fold_at); cx.add_action(Editor::unfold_lines); @@ -6185,14 +6189,31 @@ impl Editor { } pub fn go_to_definition(&mut self, _: &GoToDefinition, cx: &mut ViewContext) { - self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, cx); + self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, false, cx); } pub fn go_to_type_definition(&mut self, _: &GoToTypeDefinition, cx: &mut ViewContext) { - self.go_to_definition_of_kind(GotoDefinitionKind::Type, cx); + self.go_to_definition_of_kind(GotoDefinitionKind::Type, false, cx); } - fn go_to_definition_of_kind(&mut self, kind: GotoDefinitionKind, cx: &mut ViewContext) { + pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext) { + self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx); + } + + pub fn go_to_type_definition_split( + &mut self, + _: &GoToTypeDefinitionSplit, + cx: &mut ViewContext, + ) { + self.go_to_definition_of_kind(GotoDefinitionKind::Type, true, cx); + } + + fn go_to_definition_of_kind( + &mut self, + kind: GotoDefinitionKind, + split: bool, + cx: &mut ViewContext, + ) { let Some(workspace) = self.workspace(cx) else { return }; let buffer = self.buffer.read(cx); let head = self.selections.newest::(cx).head(); @@ -6211,7 +6232,7 @@ impl Editor { cx.spawn_labeled("Fetching Definition...", |editor, mut cx| async move { let definitions = definitions.await?; editor.update(&mut cx, |editor, cx| { - editor.navigate_to_definitions(definitions, cx); + editor.navigate_to_definitions(definitions, split, cx); })?; Ok::<(), anyhow::Error>(()) }) @@ -6221,6 +6242,7 @@ impl Editor { pub fn navigate_to_definitions( &mut self, mut definitions: Vec, + split: bool, cx: &mut ViewContext, ) { let Some(workspace) = self.workspace(cx) else { return }; @@ -6240,7 +6262,11 @@ impl Editor { } else { cx.window_context().defer(move |cx| { let target_editor: ViewHandle = workspace.update(cx, |workspace, cx| { - workspace.open_project_item(definition.target.buffer.clone(), cx) + if split { + workspace.split_project_item(definition.target.buffer.clone(), cx) + } else { + workspace.open_project_item(definition.target.buffer.clone(), cx) + } }); target_editor.update(cx, |target_editor, cx| { // When selecting a definition in a different buffer, disable the nav history @@ -6276,7 +6302,9 @@ impl Editor { .map(|definition| definition.target) .collect(); workspace.update(cx, |workspace, cx| { - Self::open_locations_in_multibuffer(workspace, locations, replica_id, title, cx) + Self::open_locations_in_multibuffer( + workspace, locations, replica_id, title, split, cx, + ) }); }); } @@ -6321,7 +6349,7 @@ impl Editor { }) .unwrap(); Self::open_locations_in_multibuffer( - workspace, locations, replica_id, title, cx, + workspace, locations, replica_id, title, false, cx, ); })?; @@ -6336,6 +6364,7 @@ impl Editor { mut locations: Vec, replica_id: ReplicaId, title: String, + split: bool, cx: &mut ViewContext, ) { // If there are multiple definitions, open them in a multibuffer @@ -6382,7 +6411,11 @@ impl Editor { cx, ); }); - workspace.add_item(Box::new(editor), cx); + if split { + workspace.split_item(Box::new(editor), cx); + } else { + workspace.add_item(Box::new(editor), cx); + } } pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext) -> Option>> { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 6420c56ece973bf9e206e1f1fc4662937c280380..4f4aa7477d6f0461795cb2e1b6366cd101e1f6df 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -156,6 +156,7 @@ impl EditorElement { event.position, event.cmd, event.shift, + event.alt, position_map.as_ref(), text_bounds, cx, @@ -308,6 +309,7 @@ impl EditorElement { position: Vector2F, cmd: bool, shift: bool, + alt: bool, position_map: &PositionMap, text_bounds: RectF, cx: &mut EventContext, @@ -324,9 +326,9 @@ impl EditorElement { if point == target_point { if shift { - go_to_fetched_type_definition(editor, point, cx); + go_to_fetched_type_definition(editor, point, alt, cx); } else { - go_to_fetched_definition(editor, point, cx); + go_to_fetched_definition(editor, point, alt, cx); } return true; diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index f3ee76dc96cbe6d9ffe1dbf8d0597331370fb04b..31df11a01959e4795738658d813ce4b23bfcafe8 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -246,23 +246,26 @@ pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext) { pub fn go_to_fetched_definition( editor: &mut Editor, point: DisplayPoint, + split: bool, cx: &mut ViewContext, ) { - go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, editor, point, cx); + go_to_fetched_definition_of_kind(LinkDefinitionKind::Symbol, editor, point, split, cx); } pub fn go_to_fetched_type_definition( editor: &mut Editor, point: DisplayPoint, + split: bool, cx: &mut ViewContext, ) { - go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, editor, point, cx); + go_to_fetched_definition_of_kind(LinkDefinitionKind::Type, editor, point, split, cx); } fn go_to_fetched_definition_of_kind( kind: LinkDefinitionKind, editor: &mut Editor, point: DisplayPoint, + split: bool, cx: &mut ViewContext, ) { let cached_definitions = editor.link_go_to_definition_state.definitions.clone(); @@ -275,7 +278,7 @@ fn go_to_fetched_definition_of_kind( cx.focus_self(); } - editor.navigate_to_definitions(cached_definitions, cx); + editor.navigate_to_definitions(cached_definitions, split, cx); } else { editor.select( SelectPhase::Begin { @@ -403,7 +406,7 @@ mod tests { }); cx.update_editor(|editor, cx| { - go_to_fetched_type_definition(editor, hover_point, cx); + go_to_fetched_type_definition(editor, hover_point, false, cx); }); requests.next().await; cx.foreground().run_until_parked(); @@ -614,7 +617,7 @@ mod tests { // Cmd click with existing definition doesn't re-request and dismisses highlight cx.update_editor(|editor, cx| { - go_to_fetched_definition(editor, hover_point, cx); + go_to_fetched_definition(editor, hover_point, false, cx); }); // Assert selection moved to to definition cx.lsp @@ -655,7 +658,7 @@ mod tests { ]))) }); cx.update_editor(|editor, cx| { - go_to_fetched_definition(editor, hover_point, cx); + go_to_fetched_definition(editor, hover_point, false, cx); }); requests.next().await; cx.foreground().run_until_parked(); diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 3f6bd83760f855c1b5dd0d3225eec6b9b8ecfa98..b6701f12d6eb057d6bb7e8ae2a91fca3dbe37f58 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -442,53 +442,71 @@ impl PickerDelegate for FileFinderDelegate { } } - fn confirm(&mut self, cx: &mut ViewContext) { + fn confirm(&mut self, secondary: bool, cx: &mut ViewContext) { if let Some(m) = self.matches.get(self.selected_index()) { if let Some(workspace) = self.workspace.upgrade(cx) { - let open_task = workspace.update(cx, |workspace, cx| match m { - Match::History(history_match) => { - let worktree_id = history_match.project.worktree_id; - if workspace - .project() - .read(cx) - .worktree_for_id(worktree_id, cx) - .is_some() - { - workspace.open_path( - ProjectPath { - worktree_id, - path: Arc::clone(&history_match.project.path), - }, - None, - true, - cx, - ) + let open_task = workspace.update(cx, move |workspace, cx| { + let split_or_open = |workspace: &mut Workspace, project_path, cx| { + if secondary { + workspace.split_path(project_path, cx) } else { - match history_match.absolute.as_ref() { - Some(abs_path) => { - workspace.open_abs_path(abs_path.to_path_buf(), false, cx) - } - None => workspace.open_path( + workspace.open_path(project_path, None, true, cx) + } + }; + match m { + Match::History(history_match) => { + let worktree_id = history_match.project.worktree_id; + if workspace + .project() + .read(cx) + .worktree_for_id(worktree_id, cx) + .is_some() + { + split_or_open( + workspace, ProjectPath { worktree_id, path: Arc::clone(&history_match.project.path), }, - None, - true, cx, - ), + ) + } else { + match history_match.absolute.as_ref() { + Some(abs_path) => { + if secondary { + workspace.split_abs_path( + abs_path.to_path_buf(), + false, + cx, + ) + } else { + workspace.open_abs_path( + abs_path.to_path_buf(), + false, + cx, + ) + } + } + None => split_or_open( + workspace, + ProjectPath { + worktree_id, + path: Arc::clone(&history_match.project.path), + }, + cx, + ), + } } } + Match::Search(m) => split_or_open( + workspace, + ProjectPath { + worktree_id: WorktreeId::from_usize(m.worktree_id), + path: m.path.clone(), + }, + cx, + ), } - Match::Search(m) => workspace.open_path( - ProjectPath { - worktree_id: WorktreeId::from_usize(m.worktree_id), - path: m.path.clone(), - }, - None, - true, - cx, - ), }); let row = self diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index 6362b8247d39b3d6dd86f339d5f72b56c94da984..b5336d5b3b7952b8c015fae7840ddd3320663424 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/crates/language_selector/src/language_selector.rs @@ -93,7 +93,7 @@ impl PickerDelegate for LanguageSelectorDelegate { self.matches.len() } - fn confirm(&mut self, cx: &mut ViewContext>) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext>) { if let Some(mat) = self.matches.get(self.selected_index) { let language_name = &self.candidates[mat.candidate_id].string; let language = self.language_registry.language_for_name(language_name); diff --git a/crates/menu/src/menu.rs b/crates/menu/src/menu.rs index 81716028b970e56aabcb77b11032ba325b0aaf38..b0f1a9c6c8dd0d1e641b6d91f70e358ed64bc3d8 100644 --- a/crates/menu/src/menu.rs +++ b/crates/menu/src/menu.rs @@ -3,6 +3,7 @@ gpui::actions!( [ Cancel, Confirm, + SecondaryConfirm, SelectPrev, SelectNext, SelectFirst, diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index f93fa100524b8371238dbe880f9519205c9c82cf..18e10678fa2a61dbeb49a14b94368fc6ee0112b3 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -177,7 +177,7 @@ impl PickerDelegate for OutlineViewDelegate { Task::ready(()) } - fn confirm(&mut self, cx: &mut ViewContext) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext) { self.prev_scroll_position.take(); self.active_editor.update(cx, |active_editor, cx| { if let Some(rows) = active_editor.highlighted_rows() { diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index d09de5320ceb8b4482f0e83c2deee0cc5b0397b3..6efa33e961b3e43911226ea04256eee64d56178b 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -7,7 +7,7 @@ use gpui::{ AnyElement, AnyViewHandle, AppContext, Axis, Entity, MouseState, Task, View, ViewContext, ViewHandle, }; -use menu::{Cancel, Confirm, SelectFirst, SelectLast, SelectNext, SelectPrev}; +use menu::{Cancel, Confirm, SecondaryConfirm, SelectFirst, SelectLast, SelectNext, SelectPrev}; use parking_lot::Mutex; use std::{cmp, sync::Arc}; use util::ResultExt; @@ -34,7 +34,7 @@ pub trait PickerDelegate: Sized + 'static { fn selected_index(&self) -> usize; fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext>); fn update_matches(&mut self, query: String, cx: &mut ViewContext>) -> Task<()>; - fn confirm(&mut self, cx: &mut ViewContext>); + fn confirm(&mut self, secondary: bool, cx: &mut ViewContext>); fn dismissed(&mut self, cx: &mut ViewContext>); fn render_match( &self, @@ -118,8 +118,8 @@ impl View for Picker { // Capture mouse events .on_down(MouseButton::Left, |_, _, _| {}) .on_up(MouseButton::Left, |_, _, _| {}) - .on_click(MouseButton::Left, move |_, picker, cx| { - picker.select_index(ix, cx); + .on_click(MouseButton::Left, move |click, picker, cx| { + picker.select_index(ix, click.cmd, cx); }) .with_cursor_style(CursorStyle::PointingHand) .into_any() @@ -175,6 +175,7 @@ impl Picker { cx.add_action(Self::select_next); cx.add_action(Self::select_prev); cx.add_action(Self::confirm); + cx.add_action(Self::secondary_confirm); cx.add_action(Self::cancel); } @@ -288,11 +289,11 @@ impl Picker { cx.notify(); } - pub fn select_index(&mut self, index: usize, cx: &mut ViewContext) { + pub fn select_index(&mut self, index: usize, cmd: bool, cx: &mut ViewContext) { if self.delegate.match_count() > 0 { self.confirmed = true; self.delegate.set_selected_index(index, cx); - self.delegate.confirm(cx); + self.delegate.confirm(cmd, cx); } } @@ -330,7 +331,12 @@ impl Picker { pub fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { self.confirmed = true; - self.delegate.confirm(cx); + self.delegate.confirm(false, cx); + } + + pub fn secondary_confirm(&mut self, _: &SecondaryConfirm, cx: &mut ViewContext) { + self.confirmed = true; + self.delegate.confirm(true, cx); } fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext) { diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index c329ae4e51d0477eaba115c72597cd6248b6432c..3e764b695db8873c73c354dd6dd96de2d8f52eb2 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -159,6 +159,9 @@ pub enum Event { entry_id: ProjectEntryId, focus_opened_item: bool, }, + SplitEntry { + entry_id: ProjectEntryId, + }, DockPositionChanged, Focus, } @@ -290,6 +293,21 @@ impl ProjectPanel { } } } + &Event::SplitEntry { entry_id } => { + if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) { + if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) { + workspace + .split_path( + ProjectPath { + worktree_id: worktree.read(cx).id(), + path: entry.path.clone(), + }, + cx, + ) + .detach_and_log_err(cx); + } + } + } _ => {} } }) @@ -620,6 +638,10 @@ impl ProjectPanel { }); } + fn split_entry(&mut self, entry_id: ProjectEntryId, cx: &mut ViewContext) { + cx.emit(Event::SplitEntry { entry_id }); + } + fn new_file(&mut self, _: &NewFile, cx: &mut ViewContext) { self.add_entry(false, cx) } @@ -1333,7 +1355,11 @@ impl ProjectPanel { if kind.is_dir() { this.toggle_expanded(entry_id, cx); } else { - this.open_entry(entry_id, event.click_count > 1, cx); + if event.cmd && event.click_count > 1 { + this.split_entry(entry_id, cx); + } else if !event.cmd { + this.open_entry(entry_id, event.click_count > 1, cx); + } } } }) diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index fc17b57c6dbf92eee74874f9cb1577f495ba8704..cbf914230d3b316370083b2cdcb3535631344dba 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -104,7 +104,7 @@ impl PickerDelegate for ProjectSymbolsDelegate { "Search project symbols...".into() } - fn confirm(&mut self, cx: &mut ViewContext) { + fn confirm(&mut self, secondary: bool, cx: &mut ViewContext) { if let Some(symbol) = self .matches .get(self.selected_match_index) @@ -122,7 +122,12 @@ impl PickerDelegate for ProjectSymbolsDelegate { .read(cx) .clip_point_utf16(symbol.range.start, Bias::Left); - let editor = workspace.open_project_item::(buffer, cx); + let editor = if secondary { + workspace.split_project_item::(buffer, cx) + } else { + workspace.open_project_item::(buffer, cx) + }; + editor.update(cx, |editor, cx| { editor.change_selections(Some(Autoscroll::center()), cx, |s| { s.select_ranges([position..position]) diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index cd512f1e574a166e5d63767f8adf6cc57ccb0b17..5bf9ba6ccfefb6f89c46a3ed1b934e931d7ce827 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -161,7 +161,7 @@ impl PickerDelegate for RecentProjectsDelegate { Task::ready(()) } - fn confirm(&mut self, cx: &mut ViewContext) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext) { if let Some((selected_match, workspace)) = self .matches .get(self.selected_index()) diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 5775f1b3e75fe3f80e5b0dd488b95f57724364cb..551000573300a16334a6a44035c91e8777af14d2 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -120,7 +120,7 @@ impl PickerDelegate for ThemeSelectorDelegate { self.matches.len() } - fn confirm(&mut self, cx: &mut ViewContext) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext) { self.selection_completed = true; let theme_name = theme::current(cx).meta.name.clone(); diff --git a/crates/vcs_menu/src/lib.rs b/crates/vcs_menu/src/lib.rs index f363f94983097758966bcdc0a7bbb4f2e31cbd8b..384b6224690a7c488cfcf87ccd0b251e3aaf7916 100644 --- a/crates/vcs_menu/src/lib.rs +++ b/crates/vcs_menu/src/lib.rs @@ -182,7 +182,7 @@ impl PickerDelegate for BranchListDelegate { }) } - fn confirm(&mut self, cx: &mut ViewContext>) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext>) { let current_pick = self.selected_index(); let Some(current_pick) = self.matches.get(current_pick).map(|pick| pick.string.clone()) else { return; diff --git a/crates/vector_store/src/modal.rs b/crates/vector_store/src/modal.rs index 9225fe8786e9173a82b32a9aedf5a7e979ff6f88..0116f1d2e509020da3dc7d8d64f102fbaf5ec222 100644 --- a/crates/vector_store/src/modal.rs +++ b/crates/vector_store/src/modal.rs @@ -51,7 +51,7 @@ impl PickerDelegate for SemanticSearchDelegate { "Search repository in natural language...".into() } - fn confirm(&mut self, cx: &mut ViewContext) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext) { if let Some(search_result) = self.matches.get(self.selected_match_index) { // Open Buffer let search_result = search_result.clone(); diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index cf24a9127e980b21241ab16e20119041b7447a9c..021e3b86a0c7c62b05d0f0474808fbb9a66b358a 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -120,7 +120,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { }) } - fn confirm(&mut self, cx: &mut ViewContext) { + fn confirm(&mut self, _: bool, cx: &mut ViewContext) { if let Some(selection) = self.matches.get(self.selected_index) { let base_keymap = BaseKeymap::from_names(&selection.string); update_settings_file::(self.fs.clone(), cx, move |setting| { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 885c686ddc62eb921bad354aa9bd825402db5e65..5781db6f2e7be404dd281e986e4456297a914c69 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1821,6 +1821,13 @@ impl Workspace { .update(cx, |pane, cx| pane.add_item(item, true, true, None, cx)); } + pub fn split_item(&mut self, item: Box, cx: &mut ViewContext) { + let new_pane = self.split_pane(self.active_pane.clone(), SplitDirection::Right, cx); + new_pane.update(cx, move |new_pane, cx| { + new_pane.add_item(item, true, true, None, cx) + }) + } + pub fn open_abs_path( &mut self, abs_path: PathBuf, @@ -1851,6 +1858,21 @@ impl Workspace { }) } + pub fn split_abs_path( + &mut self, + abs_path: PathBuf, + visible: bool, + cx: &mut ViewContext, + ) -> Task>> { + let project_path_task = + Workspace::project_path_for_path(self.project.clone(), &abs_path, visible, cx); + cx.spawn(|this, mut cx| async move { + let (_, path) = project_path_task.await?; + this.update(&mut cx, |this, cx| this.split_path(path, cx))? + .await + }) + } + pub fn open_path( &mut self, path: impl Into, @@ -1876,6 +1898,38 @@ impl Workspace { }) } + pub fn split_path( + &mut self, + path: impl Into, + cx: &mut ViewContext, + ) -> Task, anyhow::Error>> { + let pane = self.last_active_center_pane.clone().unwrap_or_else(|| { + self.panes + .first() + .expect("There must be an active pane") + .downgrade() + }); + + if let Member::Pane(center_pane) = &self.center.root { + if center_pane.read(cx).items_len() == 0 { + return self.open_path(path, Some(pane), true, cx); + } + } + + let task = self.load_path(path.into(), cx); + cx.spawn(|this, mut cx| async move { + let (project_entry_id, build_item) = task.await?; + this.update(&mut cx, move |this, cx| -> Option<_> { + let pane = pane.upgrade(cx)?; + let new_pane = this.split_pane(pane, SplitDirection::Right, cx); + new_pane.update(cx, |new_pane, cx| { + Some(new_pane.open_item(project_entry_id, true, cx, build_item)) + }) + }) + .map(|option| option.ok_or_else(|| anyhow!("pane was dropped")))? + }) + } + pub(crate) fn load_path( &mut self, path: ProjectPath, @@ -1926,6 +1980,30 @@ impl Workspace { item } + pub fn split_project_item( + &mut self, + project_item: ModelHandle, + cx: &mut ViewContext, + ) -> ViewHandle + where + T: ProjectItem, + { + use project::Item as _; + + let entry_id = project_item.read(cx).entry_id(cx); + if let Some(item) = entry_id + .and_then(|entry_id| self.active_pane().read(cx).item_for_entry(entry_id, cx)) + .and_then(|item| item.downcast()) + { + self.activate_item(&item, cx); + return item; + } + + let item = cx.add_view(|cx| T::for_project_item(self.project().clone(), project_item, cx)); + self.split_item(Box::new(item.clone()), cx); + item + } + pub fn open_shared_screen(&mut self, peer_id: PeerId, cx: &mut ViewContext) { if let Some(shared_screen) = self.shared_screen_for_peer(peer_id, &self.active_pane, cx) { self.active_pane.update(cx, |pane, cx| { @@ -1953,7 +2031,7 @@ impl Workspace { if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) { cx.focus(&pane); } else { - self.split_pane(self.active_pane.clone(), SplitDirection::Right, cx); + self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, cx); } } @@ -2006,7 +2084,7 @@ impl Workspace { match event { pane::Event::AddItem { item } => item.added_to_pane(self, pane, cx), pane::Event::Split(direction) => { - self.split_pane(pane, *direction, cx); + self.split_and_clone(pane, *direction, cx); } pane::Event::Remove => self.remove_pane(pane, cx), pane::Event::ActivateItem { local } => { @@ -2057,6 +2135,20 @@ impl Workspace { } pub fn split_pane( + &mut self, + pane_to_split: ViewHandle, + split_direction: SplitDirection, + cx: &mut ViewContext, + ) -> ViewHandle { + let new_pane = self.add_pane(cx); + self.center + .split(&pane_to_split, &new_pane, split_direction) + .unwrap(); + cx.notify(); + new_pane + } + + pub fn split_and_clone( &mut self, pane: ViewHandle, direction: SplitDirection, @@ -4246,7 +4338,7 @@ mod tests { }); workspace - .split_pane(left_pane.clone(), SplitDirection::Right, cx) + .split_and_clone(left_pane.clone(), SplitDirection::Right, cx) .unwrap(); left_pane diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 09bdbf65beef1f6f7825b112f485d0b5eec35205..1621ae276a993bfb347825970d2afa652ccd659d 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1021,7 +1021,7 @@ mod tests { // Split the pane with the first entry, then open the second entry again. workspace .update(cx, |w, cx| { - w.split_pane(w.active_pane().clone(), SplitDirection::Right, cx); + w.split_and_clone(w.active_pane().clone(), SplitDirection::Right, cx); w.open_path(file2.clone(), None, true, cx) }) .await @@ -1344,7 +1344,11 @@ mod tests { cx.dispatch_action(window_id, NewFile); workspace .update(cx, |workspace, cx| { - workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx); + workspace.split_and_clone( + workspace.active_pane().clone(), + SplitDirection::Right, + cx, + ); workspace.open_path((worktree.read(cx).id(), "the-new-name.rs"), None, true, cx) }) .await From c0b23260534380fbb2b835b48ccdfab4c45b7bc7 Mon Sep 17 00:00:00 2001 From: Alex Viscreanu Date: Fri, 14 Jul 2023 21:43:26 +0200 Subject: [PATCH 2/2] fix(flexes): reset flexes when collapsing axis Co-authored-by: Mikayla Maki --- crates/workspace/src/pane_group.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 2ece5030f31091f64c322ad8d04259a53cad35c1..52761b06c814522220342e15c01e38f253a17667 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -398,7 +398,9 @@ impl PaneAxis { } if self.members.len() == 1 { - Ok(self.members.pop()) + let result = self.members.pop(); + *self.flexes.borrow_mut() = vec![1.; self.members.len()]; + Ok(result) } else { Ok(None) }