mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
Add pull requests to git blame tooltip (#10784)
Release Notes: - Added links to GitHub pull requests to the git blame tooltips, if they are available. Screenshot: (Yes, the icon will be resized! cc @iamnbutler) ![screenshot-2024-04-19-18 31 13@2x](https://github.com/zed-industries/zed/assets/1185253/774af0b3-f587-4acc-aa1e-1846c2bec127)
This commit is contained in:
parent
70427daed2
commit
f082344747
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4317,6 +4317,7 @@ dependencies = [
|
||||
"log",
|
||||
"parking_lot",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"rope",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
1
assets/icons/pull_request.svg
Normal file
1
assets/icons/pull_request.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-git-pull-request-arrow"><circle cx="5" cy="6" r="3"/><path d="M5 9v12"/><circle cx="19" cy="18" r="3"/><path d="m15 9-3-3 3-3"/><path d="M12 6h5a2 2 0 0 1 2 2v7"/></svg>
|
After Width: | Height: | Size: 372 B |
@ -149,6 +149,11 @@ impl Render for BlameEntryTooltip {
|
||||
})
|
||||
.unwrap_or("<no commit message>".into_any());
|
||||
|
||||
let pull_request = self
|
||||
.details
|
||||
.as_ref()
|
||||
.and_then(|details| details.pull_request.clone());
|
||||
|
||||
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size;
|
||||
let message_max_height = cx.line_height() * 12 + (ui_font_size / 0.4);
|
||||
|
||||
@ -192,27 +197,51 @@ impl Render for BlameEntryTooltip {
|
||||
.justify_between()
|
||||
.child(absolute_timestamp)
|
||||
.child(
|
||||
Button::new("commit-sha-button", short_commit_id.clone())
|
||||
.style(ButtonStyle::Transparent)
|
||||
.color(Color::Muted)
|
||||
.icon(IconName::FileGit)
|
||||
.icon_color(Color::Muted)
|
||||
.icon_position(IconPosition::Start)
|
||||
.disabled(
|
||||
self.details.as_ref().map_or(true, |details| {
|
||||
details.permalink.is_none()
|
||||
}),
|
||||
)
|
||||
.when_some(
|
||||
self.details
|
||||
.as_ref()
|
||||
.and_then(|details| details.permalink.clone()),
|
||||
|this, url| {
|
||||
this.on_click(move |_, cx| {
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.when_some(pull_request, |this, pr| {
|
||||
this.child(
|
||||
Button::new(
|
||||
"pull-request-button",
|
||||
format!("#{}", pr.number),
|
||||
)
|
||||
.color(Color::Muted)
|
||||
.icon(IconName::PullRequest)
|
||||
.icon_color(Color::Muted)
|
||||
.icon_position(IconPosition::Start)
|
||||
.style(ButtonStyle::Transparent)
|
||||
.on_click(move |_, cx| {
|
||||
cx.stop_propagation();
|
||||
cx.open_url(url.as_str())
|
||||
})
|
||||
},
|
||||
cx.open_url(pr.url.as_str())
|
||||
}),
|
||||
)
|
||||
})
|
||||
.child(
|
||||
Button::new(
|
||||
"commit-sha-button",
|
||||
short_commit_id.clone(),
|
||||
)
|
||||
.style(ButtonStyle::Transparent)
|
||||
.color(Color::Muted)
|
||||
.icon(IconName::FileGit)
|
||||
.icon_color(Color::Muted)
|
||||
.icon_position(IconPosition::Start)
|
||||
.disabled(
|
||||
self.details.as_ref().map_or(true, |details| {
|
||||
details.permalink.is_none()
|
||||
}),
|
||||
)
|
||||
.when_some(
|
||||
self.details
|
||||
.as_ref()
|
||||
.and_then(|details| details.permalink.clone()),
|
||||
|this, url| {
|
||||
this.on_click(move |_, cx| {
|
||||
cx.stop_propagation();
|
||||
cx.open_url(url.as_str())
|
||||
})
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -6,6 +6,7 @@ use git::{
|
||||
blame::{Blame, BlameEntry},
|
||||
hosting_provider::HostingProvider,
|
||||
permalink::{build_commit_permalink, parse_git_remote_url},
|
||||
pull_request::{extract_pull_request, PullRequest},
|
||||
Oid,
|
||||
};
|
||||
use gpui::{Model, ModelContext, Subscription, Task};
|
||||
@ -75,6 +76,7 @@ pub struct CommitDetails {
|
||||
pub message: String,
|
||||
pub parsed_message: ParsedMarkdown,
|
||||
pub permalink: Option<Url>,
|
||||
pub pull_request: Option<PullRequest>,
|
||||
pub remote: Option<GitRemote>,
|
||||
}
|
||||
|
||||
@ -438,6 +440,10 @@ async fn parse_commit_messages(
|
||||
repo: remote.repo.to_string(),
|
||||
});
|
||||
|
||||
let pull_request = parsed_remote_url
|
||||
.as_ref()
|
||||
.and_then(|remote| extract_pull_request(remote, &message));
|
||||
|
||||
commit_details.insert(
|
||||
oid,
|
||||
CommitDetails {
|
||||
@ -445,6 +451,7 @@ async fn parse_commit_messages(
|
||||
parsed_message,
|
||||
permalink,
|
||||
remote,
|
||||
pull_request,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ time.workspace = true
|
||||
url.workspace = true
|
||||
util.workspace = true
|
||||
serde.workspace = true
|
||||
regex.workspace = true
|
||||
rope.workspace = true
|
||||
parking_lot.workspace = true
|
||||
windows.workspace = true
|
||||
|
@ -12,6 +12,7 @@ pub mod commit;
|
||||
pub mod diff;
|
||||
pub mod hosting_provider;
|
||||
pub mod permalink;
|
||||
pub mod pull_request;
|
||||
pub mod repository;
|
||||
|
||||
lazy_static! {
|
||||
|
83
crates/git/src/pull_request.rs
Normal file
83
crates/git/src/pull_request.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use lazy_static::lazy_static;
|
||||
use url::Url;
|
||||
|
||||
use crate::{hosting_provider::HostingProvider, permalink::ParsedGitRemote};
|
||||
|
||||
lazy_static! {
|
||||
static ref GITHUB_PULL_REQUEST_NUMBER: regex::Regex =
|
||||
regex::Regex::new(r"\(#(\d+)\)$").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PullRequest {
|
||||
pub number: u32,
|
||||
pub url: Url,
|
||||
}
|
||||
|
||||
pub fn extract_pull_request(remote: &ParsedGitRemote, message: &str) -> Option<PullRequest> {
|
||||
match remote.provider {
|
||||
HostingProvider::Github => {
|
||||
let line = message.lines().next()?;
|
||||
let capture = GITHUB_PULL_REQUEST_NUMBER.captures(line)?;
|
||||
let number = capture.get(1)?.as_str().parse::<u32>().ok()?;
|
||||
|
||||
let mut url = remote.provider.base_url();
|
||||
let path = format!("/{}/{}/pull/{}", remote.owner, remote.repo, number);
|
||||
url.set_path(&path);
|
||||
|
||||
Some(PullRequest { number, url })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use unindent::Unindent;
|
||||
|
||||
use crate::{
|
||||
hosting_provider::HostingProvider, permalink::ParsedGitRemote,
|
||||
pull_request::extract_pull_request,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_github_pull_requests() {
|
||||
let remote = ParsedGitRemote {
|
||||
provider: HostingProvider::Github,
|
||||
owner: "zed-industries",
|
||||
repo: "zed",
|
||||
};
|
||||
|
||||
let message = "This does not contain a pull request";
|
||||
assert!(extract_pull_request(&remote, message).is_none());
|
||||
|
||||
// Pull request number at end of first line
|
||||
let message = r#"
|
||||
project panel: do not expand collapsed worktrees on "collapse all entries" (#10687)
|
||||
|
||||
Fixes #10597
|
||||
|
||||
Release Notes:
|
||||
|
||||
- Fixed "project panel: collapse all entries" expanding collapsed worktrees.
|
||||
"#
|
||||
.unindent();
|
||||
|
||||
assert_eq!(
|
||||
extract_pull_request(&remote, &message)
|
||||
.unwrap()
|
||||
.url
|
||||
.as_str(),
|
||||
"https://github.com/zed-industries/zed/pull/10687"
|
||||
);
|
||||
|
||||
// Pull request number in middle of line, which we want to ignore
|
||||
let message = r#"
|
||||
Follow-up to #10687 to fix problems
|
||||
|
||||
See the original PR, this is a fix.
|
||||
"#
|
||||
.unindent();
|
||||
assert!(extract_pull_request(&remote, &message).is_none());
|
||||
}
|
||||
}
|
@ -121,6 +121,7 @@ pub enum IconName {
|
||||
WholeWord,
|
||||
XCircle,
|
||||
ZedXCopilot,
|
||||
PullRequest,
|
||||
}
|
||||
|
||||
impl IconName {
|
||||
@ -222,6 +223,7 @@ impl IconName {
|
||||
IconName::WholeWord => "icons/word_search.svg",
|
||||
IconName::XCircle => "icons/error.svg",
|
||||
IconName::ZedXCopilot => "icons/zed_x_copilot.svg",
|
||||
IconName::PullRequest => "icons/pull_request.svg",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user