Detailed changes
@@ -9,6 +9,7 @@
"context": "Editor",
"bindings": {
"cmd-b": "editor::GoToDefinition",
+ "alt-cmd-b": "editor::GoToDefinitionSplit",
"cmd-<": "editor::ScrollCursorCenter",
"cmd-g": [
"editor::SelectNext",
@@ -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",
@@ -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",
@@ -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",
@@ -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",
@@ -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)),
@@ -67,7 +67,7 @@ impl PickerDelegate for ContactFinderDelegate {
})
}
- fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
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) {
@@ -160,7 +160,7 @@ impl PickerDelegate for CommandPaletteDelegate {
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
- fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
if !self.matches.is_empty() {
let window_id = cx.window_id();
let focused_view_id = self.focused_view_id;
@@ -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>) {
- 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>) {
- 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<Self>) {
+ pub fn go_to_definition_split(&mut self, _: &GoToDefinitionSplit, cx: &mut ViewContext<Self>) {
+ self.go_to_definition_of_kind(GotoDefinitionKind::Symbol, true, cx);
+ }
+
+ pub fn go_to_type_definition_split(
+ &mut self,
+ _: &GoToTypeDefinitionSplit,
+ cx: &mut ViewContext<Self>,
+ ) {
+ 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<Self>,
+ ) {
let Some(workspace) = self.workspace(cx) else { return };
let buffer = self.buffer.read(cx);
let head = self.selections.newest::<usize>(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<LocationLink>,
+ split: bool,
cx: &mut ViewContext<Editor>,
) {
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<Self> = 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<Location>,
replica_id: ReplicaId,
title: String,
+ split: bool,
cx: &mut ViewContext<Workspace>,
) {
// 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<Self>) -> Option<Task<Result<()>>> {
@@ -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<Editor>,
@@ -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;
@@ -246,23 +246,26 @@ pub fn hide_link_definition(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
pub fn go_to_fetched_definition(
editor: &mut Editor,
point: DisplayPoint,
+ split: bool,
cx: &mut ViewContext<Editor>,
) {
- 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<Editor>,
) {
- 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<Editor>,
) {
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();
@@ -442,53 +442,71 @@ impl PickerDelegate for FileFinderDelegate {
}
}
- fn confirm(&mut self, cx: &mut ViewContext<FileFinder>) {
+ fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<FileFinder>) {
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
@@ -93,7 +93,7 @@ impl PickerDelegate for LanguageSelectorDelegate {
self.matches.len()
}
- fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
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);
@@ -3,6 +3,7 @@ gpui::actions!(
[
Cancel,
Confirm,
+ SecondaryConfirm,
SelectPrev,
SelectNext,
SelectFirst,
@@ -177,7 +177,7 @@ impl PickerDelegate for OutlineViewDelegate {
Task::ready(())
}
- fn confirm(&mut self, cx: &mut ViewContext<OutlineView>) {
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<OutlineView>) {
self.prev_scroll_position.take();
self.active_editor.update(cx, |active_editor, cx| {
if let Some(rows) = active_editor.highlighted_rows() {
@@ -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<Picker<Self>>);
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
- fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>);
+ fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
fn render_match(
&self,
@@ -118,8 +118,8 @@ impl<D: PickerDelegate> View for Picker<D> {
// 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<D: PickerDelegate> Picker<D> {
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<D: PickerDelegate> Picker<D> {
cx.notify();
}
- pub fn select_index(&mut self, index: usize, cx: &mut ViewContext<Self>) {
+ pub fn select_index(&mut self, index: usize, cmd: bool, cx: &mut ViewContext<Self>) {
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<D: PickerDelegate> Picker<D> {
pub fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
self.confirmed = true;
- self.delegate.confirm(cx);
+ self.delegate.confirm(false, cx);
+ }
+
+ pub fn secondary_confirm(&mut self, _: &SecondaryConfirm, cx: &mut ViewContext<Self>) {
+ self.confirmed = true;
+ self.delegate.confirm(true, cx);
}
fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
@@ -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<Self>) {
+ cx.emit(Event::SplitEntry { entry_id });
+ }
+
fn new_file(&mut self, _: &NewFile, cx: &mut ViewContext<Self>) {
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);
+ }
}
}
})
@@ -104,7 +104,7 @@ impl PickerDelegate for ProjectSymbolsDelegate {
"Search project symbols...".into()
}
- fn confirm(&mut self, cx: &mut ViewContext<ProjectSymbols>) {
+ fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<ProjectSymbols>) {
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::<Editor>(buffer, cx);
+ let editor = if secondary {
+ workspace.split_project_item::<Editor>(buffer, cx)
+ } else {
+ workspace.open_project_item::<Editor>(buffer, cx)
+ };
+
editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::center()), cx, |s| {
s.select_ranges([position..position])
@@ -161,7 +161,7 @@ impl PickerDelegate for RecentProjectsDelegate {
Task::ready(())
}
- fn confirm(&mut self, cx: &mut ViewContext<RecentProjects>) {
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<RecentProjects>) {
if let Some((selected_match, workspace)) = self
.matches
.get(self.selected_index())
@@ -120,7 +120,7 @@ impl PickerDelegate for ThemeSelectorDelegate {
self.matches.len()
}
- fn confirm(&mut self, cx: &mut ViewContext<ThemeSelector>) {
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<ThemeSelector>) {
self.selection_completed = true;
let theme_name = theme::current(cx).meta.name.clone();
@@ -182,7 +182,7 @@ impl PickerDelegate for BranchListDelegate {
})
}
- fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
let current_pick = self.selected_index();
let Some(current_pick) = self.matches.get(current_pick).map(|pick| pick.string.clone()) else {
return;
@@ -51,7 +51,7 @@ impl PickerDelegate for SemanticSearchDelegate {
"Search repository in natural language...".into()
}
- fn confirm(&mut self, cx: &mut ViewContext<SemanticSearch>) {
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<SemanticSearch>) {
if let Some(search_result) = self.matches.get(self.selected_match_index) {
// Open Buffer
let search_result = search_result.clone();
@@ -120,7 +120,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate {
})
}
- fn confirm(&mut self, cx: &mut ViewContext<BaseKeymapSelector>) {
+ fn confirm(&mut self, _: bool, cx: &mut ViewContext<BaseKeymapSelector>) {
if let Some(selection) = self.matches.get(self.selected_index) {
let base_keymap = BaseKeymap::from_names(&selection.string);
update_settings_file::<BaseKeymap>(self.fs.clone(), cx, move |setting| {
@@ -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<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
+ 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<Self>,
+ ) -> Task<anyhow::Result<Box<dyn ItemHandle>>> {
+ 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<ProjectPath>,
@@ -1876,6 +1898,38 @@ impl Workspace {
})
}
+ pub fn split_path(
+ &mut self,
+ path: impl Into<ProjectPath>,
+ cx: &mut ViewContext<Self>,
+ ) -> Task<Result<Box<dyn ItemHandle>, 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<T>(
+ &mut self,
+ project_item: ModelHandle<T::Item>,
+ cx: &mut ViewContext<Self>,
+ ) -> ViewHandle<T>
+ 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<Self>) {
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<Pane>,
+ split_direction: SplitDirection,
+ cx: &mut ViewContext<Self>,
+ ) -> ViewHandle<Pane> {
+ 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<Pane>,
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
@@ -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