mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-24 19:10:24 +00:00
vcs_menu: Streamline branch creation from branch selector (#18712)
Some checks are pending
CI / Check formatting and spelling (push) Waiting to run
CI / (macOS) Run Clippy and tests (push) Waiting to run
CI / (Linux) Run Clippy and tests (push) Waiting to run
CI / (Windows) Run Clippy and tests (push) Waiting to run
CI / Create a macOS bundle (push) Blocked by required conditions
CI / Create a Linux bundle (push) Blocked by required conditions
CI / Create arm64 Linux bundle (push) Blocked by required conditions
Deploy Docs / Deploy Docs (push) Waiting to run
Docs / Check formatting (push) Waiting to run
Some checks are pending
CI / Check formatting and spelling (push) Waiting to run
CI / (macOS) Run Clippy and tests (push) Waiting to run
CI / (Linux) Run Clippy and tests (push) Waiting to run
CI / (Windows) Run Clippy and tests (push) Waiting to run
CI / Create a macOS bundle (push) Blocked by required conditions
CI / Create a Linux bundle (push) Blocked by required conditions
CI / Create arm64 Linux bundle (push) Blocked by required conditions
Deploy Docs / Deploy Docs (push) Waiting to run
Docs / Check formatting (push) Waiting to run
This PR streamlines the branch creation from the branch selector when searching for a branch that does not exist. The branch selector will show the available branches, as it does today: <img width="576" alt="Screenshot 2024-10-03 at 4 01 25 PM" src="https://github.com/user-attachments/assets/e1904f5b-4aad-4f88-901d-ab9422ec18bb"> When entering the name of a branch that does not exist, the picker will be populated with an entry to create a new branch: <img width="570" alt="Screenshot 2024-10-03 at 4 01 37 PM" src="https://github.com/user-attachments/assets/07f8d12c-9422-4fd8-a6dc-ae450e297a13"> Selecting that entry will create the branch and switch to it. Release Notes: - Streamlined creating a new branch from the branch selector.
This commit is contained in:
parent
8d6fa9526e
commit
6635758009
1 changed files with 75 additions and 81 deletions
|
@ -74,8 +74,23 @@ impl Render for BranchList {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum BranchEntry {
|
||||
Branch(StringMatch),
|
||||
NewBranch { name: String },
|
||||
}
|
||||
|
||||
impl BranchEntry {
|
||||
fn name(&self) -> &str {
|
||||
match self {
|
||||
Self::Branch(branch) => &branch.string,
|
||||
Self::NewBranch { name } => &name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BranchListDelegate {
|
||||
matches: Vec<StringMatch>,
|
||||
matches: Vec<BranchEntry>,
|
||||
all_branches: Vec<Branch>,
|
||||
workspace: View<Workspace>,
|
||||
selected_index: usize,
|
||||
|
@ -194,8 +209,14 @@ impl PickerDelegate for BranchListDelegate {
|
|||
picker
|
||||
.update(&mut cx, |picker, _| {
|
||||
let delegate = &mut picker.delegate;
|
||||
delegate.matches = matches;
|
||||
delegate.matches = matches.into_iter().map(BranchEntry::Branch).collect();
|
||||
if delegate.matches.is_empty() {
|
||||
if !query.is_empty() {
|
||||
delegate.matches.push(BranchEntry::NewBranch {
|
||||
name: query.trim().replace(' ', "-"),
|
||||
});
|
||||
}
|
||||
|
||||
delegate.selected_index = 0;
|
||||
} else {
|
||||
delegate.selected_index =
|
||||
|
@ -208,32 +229,44 @@ impl PickerDelegate for BranchListDelegate {
|
|||
}
|
||||
|
||||
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 {
|
||||
let Some(branch) = self.matches.get(self.selected_index()) else {
|
||||
return;
|
||||
};
|
||||
cx.spawn(|picker, mut cx| async move {
|
||||
picker
|
||||
.update(&mut cx, |this, cx| {
|
||||
let project = this.delegate.workspace.read(cx).project().read(cx);
|
||||
let repo = project
|
||||
.get_first_worktree_root_repo(cx)
|
||||
.context("failed to get root repository for first worktree")?;
|
||||
let status = repo
|
||||
.change_branch(¤t_pick);
|
||||
if status.is_err() {
|
||||
this.delegate.display_error_toast(format!("Failed to checkout branch '{current_pick}', check for conflicts or unstashed files"), cx);
|
||||
status?;
|
||||
}
|
||||
cx.emit(DismissEvent);
|
||||
cx.spawn({
|
||||
let branch = branch.clone();
|
||||
|picker, mut cx| async move {
|
||||
picker
|
||||
.update(&mut cx, |this, cx| {
|
||||
let project = this.delegate.workspace.read(cx).project().read(cx);
|
||||
let repo = project
|
||||
.get_first_worktree_root_repo(cx)
|
||||
.context("failed to get root repository for first worktree")?;
|
||||
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})
|
||||
.log_err();
|
||||
let branch_to_checkout = match branch {
|
||||
BranchEntry::Branch(branch) => branch.string,
|
||||
BranchEntry::NewBranch { name: branch_name } => {
|
||||
let status = repo.create_branch(&branch_name);
|
||||
if status.is_err() {
|
||||
this.delegate.display_error_toast(format!("Failed to create branch '{branch_name}', check for conflicts or unstashed files"), cx);
|
||||
status?;
|
||||
}
|
||||
|
||||
branch_name
|
||||
}
|
||||
};
|
||||
|
||||
let status = repo.change_branch(&branch_to_checkout);
|
||||
if status.is_err() {
|
||||
this.delegate.display_error_toast(format!("Failed to checkout branch '{branch_to_checkout}', check for conflicts or unstashed files"), cx);
|
||||
status?;
|
||||
}
|
||||
|
||||
cx.emit(DismissEvent);
|
||||
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
@ -250,19 +283,28 @@ impl PickerDelegate for BranchListDelegate {
|
|||
) -> Option<Self::ListItem> {
|
||||
let hit = &self.matches[ix];
|
||||
let shortened_branch_name =
|
||||
util::truncate_and_trailoff(&hit.string, self.branch_name_trailoff_after);
|
||||
let highlights: Vec<_> = hit
|
||||
.positions
|
||||
.iter()
|
||||
.filter(|index| index < &&self.branch_name_trailoff_after)
|
||||
.copied()
|
||||
.collect();
|
||||
util::truncate_and_trailoff(&hit.name(), self.branch_name_trailoff_after);
|
||||
|
||||
Some(
|
||||
ListItem::new(SharedString::from(format!("vcs-menu-{ix}")))
|
||||
.inset(true)
|
||||
.spacing(ListItemSpacing::Sparse)
|
||||
.selected(selected)
|
||||
.start_slot(HighlightedLabel::new(shortened_branch_name, highlights)),
|
||||
.map(|parent| match hit {
|
||||
BranchEntry::Branch(branch) => {
|
||||
let highlights: Vec<_> = branch
|
||||
.positions
|
||||
.iter()
|
||||
.filter(|index| index < &&self.branch_name_trailoff_after)
|
||||
.copied()
|
||||
.collect();
|
||||
|
||||
parent.child(HighlightedLabel::new(shortened_branch_name, highlights))
|
||||
}
|
||||
BranchEntry::NewBranch { name } => {
|
||||
parent.child(Label::new(format!("Create branch '{name}'")))
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -289,52 +331,4 @@ impl PickerDelegate for BranchListDelegate {
|
|||
};
|
||||
Some(v_flex().mt_1().child(label).into_any_element())
|
||||
}
|
||||
|
||||
fn render_footer(&self, cx: &mut ViewContext<Picker<Self>>) -> Option<AnyElement> {
|
||||
if self.last_query.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(
|
||||
h_flex()
|
||||
.p_2()
|
||||
.border_t_1()
|
||||
.border_color(cx.theme().colors().border_variant)
|
||||
.justify_end()
|
||||
.child(h_flex().w_full())
|
||||
.child(
|
||||
Button::new("branch-picker-create-branch-button", "Create Branch")
|
||||
.icon(IconName::Plus)
|
||||
.icon_size(IconSize::Small)
|
||||
.icon_color(Color::Muted)
|
||||
.icon_position(IconPosition::Start)
|
||||
.on_click(cx.listener(|_, _, cx| {
|
||||
cx.spawn(|picker, mut cx| async move {
|
||||
picker.update(&mut cx, |this, cx| {
|
||||
let project =
|
||||
this.delegate.workspace.read(cx).project().read(cx);
|
||||
let current_pick = &this.delegate.last_query;
|
||||
let repo = project.get_first_worktree_root_repo(cx).context(
|
||||
"failed to get root repository for first worktree",
|
||||
)?;
|
||||
let status = repo.create_branch(current_pick);
|
||||
if status.is_err() {
|
||||
this.delegate.display_error_toast(format!("Failed to create branch '{current_pick}', check for conflicts or unstashed files"), cx);
|
||||
status?;
|
||||
}
|
||||
let status = repo.change_branch(current_pick);
|
||||
if status.is_err() {
|
||||
this.delegate.display_error_toast(format!("Failed to check branch '{current_pick}', check for conflicts or unstashed files"), cx);
|
||||
status?;
|
||||
}
|
||||
this.cancel(&Default::default(), cx);
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}))
|
||||
)
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue