mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-03 17:44:30 +00:00
commit
e69240cf13
4 changed files with 147 additions and 43 deletions
|
@ -39,6 +39,9 @@ pub trait GitRepository: Send {
|
|||
fn change_branch(&self, _: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn create_branch(&self, _: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for dyn GitRepository {
|
||||
|
@ -152,6 +155,12 @@ impl GitRepository for LibGitRepository {
|
|||
)?;
|
||||
Ok(())
|
||||
}
|
||||
fn create_branch(&self, name: &str) -> Result<()> {
|
||||
let current_commit = self.head()?.peel_to_commit()?;
|
||||
self.branch(name, ¤t_commit, false)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_status(status: git2::Status) -> Option<GitFileStatus> {
|
||||
|
|
|
@ -586,7 +586,7 @@ pub struct Picker {
|
|||
pub no_matches: ContainedLabel,
|
||||
pub item: Toggleable<Interactive<ContainedLabel>>,
|
||||
pub header: ContainedLabel,
|
||||
pub footer: ContainedLabel,
|
||||
pub footer: Interactive<ContainedLabel>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Default, JsonSchema)]
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
use anyhow::{anyhow, bail, Result};
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{actions, elements::*, AppContext, MouseState, Task, ViewContext, ViewHandle};
|
||||
use gpui::{
|
||||
actions,
|
||||
elements::*,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AppContext, MouseState, Task, ViewContext, ViewHandle,
|
||||
};
|
||||
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||
use std::{ops::Not, sync::Arc};
|
||||
use util::ResultExt;
|
||||
|
@ -70,6 +75,14 @@ pub struct BranchListDelegate {
|
|||
branch_name_trailoff_after: usize,
|
||||
}
|
||||
|
||||
impl BranchListDelegate {
|
||||
fn display_error_toast(&self, message: String, cx: &mut ViewContext<BranchList>) {
|
||||
const GIT_CHECKOUT_FAILURE_ID: usize = 2048;
|
||||
self.workspace.update(cx, |model, ctx| {
|
||||
model.show_toast(Toast::new(GIT_CHECKOUT_FAILURE_ID, message), ctx)
|
||||
});
|
||||
}
|
||||
}
|
||||
impl PickerDelegate for BranchListDelegate {
|
||||
fn placeholder_text(&self) -> Arc<str> {
|
||||
"Select branch...".into()
|
||||
|
@ -171,40 +184,39 @@ impl PickerDelegate for BranchListDelegate {
|
|||
let current_pick = self.selected_index();
|
||||
let current_pick = self.matches[current_pick].string.clone();
|
||||
cx.spawn(|picker, mut cx| async move {
|
||||
picker.update(&mut cx, |this, cx| {
|
||||
let project = this.delegate().workspace.read(cx).project().read(cx);
|
||||
let mut cwd = project
|
||||
.visible_worktrees(cx)
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("There are no visisible worktrees."))?
|
||||
.read(cx)
|
||||
.abs_path()
|
||||
.to_path_buf();
|
||||
cwd.push(".git");
|
||||
let status = project
|
||||
.fs()
|
||||
.open_repo(&cwd)
|
||||
.ok_or_else(|| anyhow!("Could not open repository at path `{}`", cwd.as_os_str().to_string_lossy()))?
|
||||
.lock()
|
||||
.change_branch(¤t_pick);
|
||||
if status.is_err() {
|
||||
const GIT_CHECKOUT_FAILURE_ID: usize = 2048;
|
||||
this.delegate().workspace.update(cx, |model, ctx| {
|
||||
model.show_toast(
|
||||
Toast::new(
|
||||
GIT_CHECKOUT_FAILURE_ID,
|
||||
format!("Failed to checkout branch '{current_pick}', check for conflicts or unstashed files"),
|
||||
),
|
||||
ctx,
|
||||
)
|
||||
});
|
||||
status?;
|
||||
}
|
||||
cx.emit(PickerEvent::Dismiss);
|
||||
picker
|
||||
.update(&mut cx, |this, cx| {
|
||||
let project = this.delegate().workspace.read(cx).project().read(cx);
|
||||
let mut cwd = project
|
||||
.visible_worktrees(cx)
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("There are no visisible worktrees."))?
|
||||
.read(cx)
|
||||
.abs_path()
|
||||
.to_path_buf();
|
||||
cwd.push(".git");
|
||||
let status = project
|
||||
.fs()
|
||||
.open_repo(&cwd)
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Could not open repository at path `{}`",
|
||||
cwd.as_os_str().to_string_lossy()
|
||||
)
|
||||
})?
|
||||
.lock()
|
||||
.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(PickerEvent::Dismiss);
|
||||
|
||||
Ok::<(), anyhow::Error>(())
|
||||
}).log_err();
|
||||
}).detach();
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})
|
||||
.log_err();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
|
||||
|
@ -270,4 +282,61 @@ impl PickerDelegate for BranchListDelegate {
|
|||
};
|
||||
Some(label.into_any())
|
||||
}
|
||||
fn render_footer(
|
||||
&self,
|
||||
cx: &mut ViewContext<Picker<Self>>,
|
||||
) -> Option<AnyElement<Picker<Self>>> {
|
||||
if !self.last_query.is_empty() {
|
||||
let theme = &theme::current(cx);
|
||||
let style = theme.picker.footer.clone();
|
||||
enum BranchCreateButton {}
|
||||
Some(
|
||||
Flex::row().with_child(MouseEventHandler::<BranchCreateButton, _>::new(0, cx, |state, _| {
|
||||
let style = style.style_for(state);
|
||||
Label::new("Create branch", style.label.clone())
|
||||
.contained()
|
||||
.with_style(style.container)
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_down(MouseButton::Left, |_, _, 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 mut cwd = project
|
||||
.visible_worktrees(cx)
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("There are no visisible worktrees."))?
|
||||
.read(cx)
|
||||
.abs_path()
|
||||
.to_path_buf();
|
||||
cwd.push(".git");
|
||||
let repo = project
|
||||
.fs()
|
||||
.open_repo(&cwd)
|
||||
.ok_or_else(|| anyhow!("Could not open repository at path `{}`", cwd.as_os_str().to_string_lossy()))?;
|
||||
let repo = repo
|
||||
.lock();
|
||||
let status = repo
|
||||
.create_branch(¤t_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(¤t_pick);
|
||||
if status.is_err() {
|
||||
this.delegate().display_error_toast(format!("Failed to chec branch '{current_pick}', check for conflicts or unstashed files"), cx);
|
||||
status?;
|
||||
}
|
||||
cx.emit(PickerEvent::Dismiss);
|
||||
Ok::<(), anyhow::Error>(())
|
||||
})
|
||||
}).detach();
|
||||
})).aligned().right()
|
||||
.into_any(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,14 +119,40 @@ export default function picker(): any {
|
|||
right: 8,
|
||||
},
|
||||
},
|
||||
footer: {
|
||||
text: text(theme.lowest, "sans", "variant", { size: "xs" }),
|
||||
margin: {
|
||||
top: 1,
|
||||
left: 8,
|
||||
right: 8,
|
||||
footer: interactive({
|
||||
base: {
|
||||
text: text(theme.lowest, "sans", "base", { size: "xs" }),
|
||||
padding: {
|
||||
bottom: 4,
|
||||
left: 12,
|
||||
right: 12,
|
||||
top: 4,
|
||||
},
|
||||
margin: {
|
||||
top: 1,
|
||||
left: 4,
|
||||
right: 4,
|
||||
},
|
||||
corner_radius: 8,
|
||||
background: with_opacity(
|
||||
background(theme.lowest, "active"),
|
||||
0.5
|
||||
),
|
||||
},
|
||||
|
||||
}
|
||||
state: {
|
||||
hovered: {
|
||||
background: with_opacity(
|
||||
background(theme.lowest, "hovered"),
|
||||
0.5
|
||||
),
|
||||
},
|
||||
clicked: {
|
||||
background: with_opacity(
|
||||
background(theme.lowest, "pressed"),
|
||||
0.5
|
||||
),
|
||||
},
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue