Improve stash picker ui
This commit is contained in:
parent
d03557748b
commit
4f11b9ef56
5 changed files with 80 additions and 12 deletions
|
@ -994,7 +994,7 @@ impl GitRepository for RealGitRepository {
|
|||
.spawn(async move {
|
||||
let output = new_std_command(&git_binary_path)
|
||||
.current_dir(working_directory?)
|
||||
.args(&["stash", "list", "--pretty=%gd:%H:%s"])
|
||||
.args(&["stash", "list", "--pretty=format:%gd%x00%H%x00%ct%x00%s"])
|
||||
.output()?;
|
||||
if output.status.success() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
|
|
|
@ -7,6 +7,8 @@ pub struct StashEntry {
|
|||
pub index: usize,
|
||||
pub oid: Oid,
|
||||
pub message: String,
|
||||
pub branch: Option<String>,
|
||||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
|
||||
|
@ -28,7 +30,7 @@ impl FromStr for GitStash {
|
|||
let entries = s
|
||||
.split('\n')
|
||||
.filter_map(|entry| {
|
||||
let mut parts = entry.splitn(3, ':');
|
||||
let mut parts = entry.splitn(4, '\0');
|
||||
let raw_idx = parts.next().and_then(|i| {
|
||||
let trimmed = i.trim();
|
||||
if trimmed.starts_with("stash@{") && trimmed.ends_with('}') {
|
||||
|
@ -40,15 +42,21 @@ impl FromStr for GitStash {
|
|||
}
|
||||
});
|
||||
let raw_oid = parts.next();
|
||||
let raw_date = parts.next().and_then(|d| d.parse().ok());
|
||||
let message = parts.next();
|
||||
|
||||
if let (Some(raw_idx), Some(raw_oid), Some(message)) = (raw_idx, raw_oid, message) {
|
||||
if let (Some(raw_idx), Some(raw_oid), Some(raw_date), Some(message)) =
|
||||
(raw_idx, raw_oid, raw_date, message)
|
||||
{
|
||||
let (branch, message) = parse_stash_entry(message);
|
||||
let index = raw_idx.parse::<usize>().ok()?;
|
||||
let oid = Oid::from_str(raw_oid).ok()?;
|
||||
let entry = StashEntry {
|
||||
index,
|
||||
oid,
|
||||
message: message.to_string(),
|
||||
branch: branch.map(Into::into),
|
||||
timestamp: raw_date,
|
||||
};
|
||||
return Some(entry);
|
||||
}
|
||||
|
@ -60,3 +68,26 @@ impl FromStr for GitStash {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_stash_entry(input: &str) -> (Option<&str>, &str) {
|
||||
// Try to match "WIP on <branch>: <message>" pattern
|
||||
if let Some(stripped) = input.strip_prefix("WIP on ") {
|
||||
if let Some(colon_pos) = stripped.find(": ") {
|
||||
let branch = &stripped[..colon_pos];
|
||||
let message = &stripped[colon_pos + 2..];
|
||||
return (Some(branch), message);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to match "On <branch>: <message>" pattern
|
||||
if let Some(stripped) = input.strip_prefix("On ") {
|
||||
if let Some(colon_pos) = stripped.find(": ") {
|
||||
let branch = &stripped[..colon_pos];
|
||||
let message = &stripped[colon_pos + 2..];
|
||||
return (Some(branch), message);
|
||||
}
|
||||
}
|
||||
|
||||
// Edge case: format doesn't match, return None for branch and full string as message
|
||||
(None, input)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ use gpui::{
|
|||
use picker::{Picker, PickerDelegate, PickerEditorPosition};
|
||||
use project::git_store::{Repository, RepositoryEvent};
|
||||
use std::sync::Arc;
|
||||
use ui::{HighlightedLabel, KeyBinding, ListItem, ListItemSpacing, prelude::*};
|
||||
use time::OffsetDateTime;
|
||||
use time_format::format_local_timestamp;
|
||||
use ui::{HighlightedLabel, KeyBinding, ListItem, ListItemSpacing, Tooltip, prelude::*};
|
||||
use util::ResultExt;
|
||||
use workspace::notifications::DetachAndPromptErr;
|
||||
use workspace::{ModalView, Workspace};
|
||||
|
@ -220,6 +222,10 @@ impl StashListDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_message(ix: usize, message: &String) -> String {
|
||||
format!("#{}: {}", ix, message)
|
||||
}
|
||||
|
||||
fn drop_stash_at(&self, ix: usize, window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||
let Some(entry_match) = self.matches.get(ix) else {
|
||||
return;
|
||||
|
@ -310,7 +316,12 @@ impl PickerDelegate for StashListDelegate {
|
|||
let candidates = all_stash_entries
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(ix, entry)| StringMatchCandidate::new(ix, &entry.message))
|
||||
.map(|(ix, entry)| {
|
||||
StringMatchCandidate::new(
|
||||
ix,
|
||||
&Self::format_message(entry.index, &entry.message),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<StringMatchCandidate>>();
|
||||
fuzzy::match_strings(
|
||||
&candidates,
|
||||
|
@ -367,7 +378,8 @@ impl PickerDelegate for StashListDelegate {
|
|||
) -> Option<Self::ListItem> {
|
||||
let entry_match = &self.matches[ix];
|
||||
|
||||
let mut stash_message = entry_match.entry.message.clone();
|
||||
let mut stash_message =
|
||||
Self::format_message(entry_match.entry.index, &entry_match.entry.message);
|
||||
let mut positions = entry_match.positions.clone();
|
||||
|
||||
if stash_message.is_ascii() {
|
||||
|
@ -394,10 +406,28 @@ impl PickerDelegate for StashListDelegate {
|
|||
|
||||
let stash_name = HighlightedLabel::new(stash_message, positions).into_any_element();
|
||||
|
||||
let stash_index_label = Label::new(format!("stash@{{{}}}", entry_match.entry.index))
|
||||
let stash_index_label = Label::new(
|
||||
entry_match
|
||||
.entry
|
||||
.branch
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
)
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted);
|
||||
|
||||
let absolute_timestamp = format_local_timestamp(
|
||||
OffsetDateTime::from_unix_timestamp(entry_match.entry.timestamp)
|
||||
.unwrap_or(OffsetDateTime::now_utc()),
|
||||
OffsetDateTime::now_utc(),
|
||||
time_format::TimestampFormat::MediumAbsolute,
|
||||
);
|
||||
let tooltip_text = format!(
|
||||
"stash@{{{}}} created {}",
|
||||
entry_match.entry.index, absolute_timestamp
|
||||
);
|
||||
|
||||
Some(
|
||||
ListItem::new(SharedString::from(format!("stash-{ix}")))
|
||||
.inset(true)
|
||||
|
@ -412,7 +442,8 @@ impl PickerDelegate for StashListDelegate {
|
|||
.child(stash_name)
|
||||
.child(stash_index_label.into_element()),
|
||||
),
|
||||
),
|
||||
)
|
||||
.tooltip(Tooltip::text(tooltip_text)),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2928,7 +2928,9 @@ pub fn stash_to_proto(entry: &StashEntry) -> proto::StashEntry {
|
|||
proto::StashEntry {
|
||||
oid: entry.oid.as_bytes().to_vec(),
|
||||
message: entry.message.clone(),
|
||||
index: entry.index as i64,
|
||||
branch: entry.branch.clone(),
|
||||
index: entry.index as u64,
|
||||
timestamp: entry.timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2937,6 +2939,8 @@ pub fn proto_to_stash(entry: &proto::StashEntry) -> Result<StashEntry> {
|
|||
oid: Oid::from_bytes(&entry.oid)?,
|
||||
message: entry.message.clone(),
|
||||
index: entry.index as usize,
|
||||
branch: entry.branch.clone(),
|
||||
timestamp: entry.timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -287,7 +287,9 @@ message StatusEntry {
|
|||
message StashEntry {
|
||||
bytes oid = 1;
|
||||
string message = 2;
|
||||
int64 index = 3;
|
||||
optional string branch = 3;
|
||||
uint64 index = 4;
|
||||
int64 timestamp = 5;
|
||||
}
|
||||
|
||||
message Stage {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue