mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-07 20:39:04 +03:00
chat: auto detect links (#8028)
@ConradIrwin here's our current implementation for auto detecting links in the chat. We also fixed an edge case where the close reply to preview button was cut off (rendered off screen). Release Notes: - Added auto detection for links in the chat panel. --------- Co-authored-by: Remco Smits <62463826+RemcoSmitsDev@users.noreply.github.com>
This commit is contained in:
parent
1e44bac418
commit
3ef8a9910d
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -7555,6 +7555,7 @@ dependencies = [
|
||||
"gpui",
|
||||
"language",
|
||||
"lazy_static",
|
||||
"linkify",
|
||||
"pulldown-cmark",
|
||||
"smallvec",
|
||||
"smol",
|
||||
|
@ -199,6 +199,7 @@ indoc = "1"
|
||||
# We explicitly disable a http2 support in isahc.
|
||||
isahc = { version = "1.7.2", default-features = false, features = ["static-curl", "text-decoding"] }
|
||||
lazy_static = "1.4.0"
|
||||
linkify = "0.10.0"
|
||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||
ordered-float = "2.1.1"
|
||||
parking_lot = "0.11.1"
|
||||
|
@ -371,9 +371,9 @@ impl ChatPanel {
|
||||
.px_1()
|
||||
.py_0p5()
|
||||
.mb_1()
|
||||
.overflow_hidden()
|
||||
.child(
|
||||
div()
|
||||
.overflow_hidden()
|
||||
.max_h_12()
|
||||
.child(reply_to_message_body.element(body_element_id, cx)),
|
||||
),
|
||||
@ -840,18 +840,21 @@ impl Render for ChatPanel {
|
||||
|
||||
el.when_some(reply_message, |el, reply_message| {
|
||||
el.child(
|
||||
div()
|
||||
h_flex()
|
||||
.when(!self.is_scrolled_to_bottom, |el| {
|
||||
el.border_t_1().border_color(cx.theme().colors().border)
|
||||
})
|
||||
.flex()
|
||||
.w_full()
|
||||
.items_start()
|
||||
.justify_between()
|
||||
.overflow_hidden()
|
||||
.items_start()
|
||||
.py_1()
|
||||
.px_2()
|
||||
.bg(cx.theme().colors().background)
|
||||
.child(self.render_replied_to_message(None, &reply_message, cx))
|
||||
.child(
|
||||
div().flex_shrink().overflow_hidden().child(
|
||||
self.render_replied_to_message(None, &reply_message, cx),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("close-reply-preview", IconName::Close)
|
||||
.shape(ui::IconButtonShape::Square)
|
||||
@ -1094,6 +1097,107 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_render_markdown_with_auto_detect_links() {
|
||||
let language_registry = Arc::new(LanguageRegistry::test());
|
||||
let message = channel::ChannelMessage {
|
||||
id: ChannelMessageId::Saved(0),
|
||||
body: "Here is a link https://zed.dev to zeds website".to_string(),
|
||||
timestamp: OffsetDateTime::now_utc(),
|
||||
sender: Arc::new(client::User {
|
||||
github_login: "fgh".into(),
|
||||
avatar_uri: "avatar_fgh".into(),
|
||||
id: 103,
|
||||
}),
|
||||
nonce: 5,
|
||||
mentions: Vec::new(),
|
||||
reply_to_message_id: None,
|
||||
};
|
||||
|
||||
let message = ChatPanel::render_markdown_with_mentions(&language_registry, 102, &message);
|
||||
|
||||
// Note that the "'" was replaced with ’ due to smart punctuation.
|
||||
let (body, ranges) =
|
||||
marked_text_ranges("Here is a link «https://zed.dev» to zeds website", false);
|
||||
assert_eq!(message.text, body);
|
||||
assert_eq!(1, ranges.len());
|
||||
assert_eq!(
|
||||
message.highlights,
|
||||
vec![(
|
||||
ranges[0].clone(),
|
||||
HighlightStyle {
|
||||
underline: Some(gpui::UnderlineStyle {
|
||||
thickness: 1.0.into(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
),]
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_render_markdown_with_auto_detect_links_and_additional_formatting() {
|
||||
let language_registry = Arc::new(LanguageRegistry::test());
|
||||
let message = channel::ChannelMessage {
|
||||
id: ChannelMessageId::Saved(0),
|
||||
body: "**Here is a link https://zed.dev to zeds website**".to_string(),
|
||||
timestamp: OffsetDateTime::now_utc(),
|
||||
sender: Arc::new(client::User {
|
||||
github_login: "fgh".into(),
|
||||
avatar_uri: "avatar_fgh".into(),
|
||||
id: 103,
|
||||
}),
|
||||
nonce: 5,
|
||||
mentions: Vec::new(),
|
||||
reply_to_message_id: None,
|
||||
};
|
||||
|
||||
let message = ChatPanel::render_markdown_with_mentions(&language_registry, 102, &message);
|
||||
|
||||
// Note that the "'" was replaced with ’ due to smart punctuation.
|
||||
let (body, ranges) = marked_text_ranges(
|
||||
"«Here is a link »«https://zed.dev»« to zeds website»",
|
||||
false,
|
||||
);
|
||||
assert_eq!(message.text, body);
|
||||
assert_eq!(3, ranges.len());
|
||||
assert_eq!(
|
||||
message.highlights,
|
||||
vec![
|
||||
(
|
||||
ranges[0].clone(),
|
||||
HighlightStyle {
|
||||
font_weight: Some(gpui::FontWeight::BOLD),
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
),
|
||||
(
|
||||
ranges[1].clone(),
|
||||
HighlightStyle {
|
||||
font_weight: Some(gpui::FontWeight::BOLD),
|
||||
underline: Some(gpui::UnderlineStyle {
|
||||
thickness: 1.0.into(),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
),
|
||||
(
|
||||
ranges[2].clone(),
|
||||
HighlightStyle {
|
||||
font_weight: Some(gpui::FontWeight::BOLD),
|
||||
..Default::default()
|
||||
}
|
||||
.into()
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_locale() {
|
||||
let reference = create_offset_datetime(1990, 4, 12, 16, 45, 0);
|
||||
|
@ -40,7 +40,7 @@ indoc = "1.0.4"
|
||||
itertools = "0.10"
|
||||
language.workspace = true
|
||||
lazy_static.workspace = true
|
||||
linkify = "0.10.0"
|
||||
linkify.workspace = true
|
||||
log.workspace = true
|
||||
lsp.workspace = true
|
||||
multi_buffer.workspace = true
|
||||
|
@ -22,6 +22,7 @@ futures.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
lazy_static.workspace = true
|
||||
linkify.workspace = true
|
||||
pulldown-cmark.workspace = true
|
||||
smallvec.workspace = true
|
||||
smol.workspace = true
|
||||
|
@ -175,19 +175,54 @@ pub fn render_markdown_mut(
|
||||
if italic_depth > 0 {
|
||||
style.font_style = Some(FontStyle::Italic);
|
||||
}
|
||||
if let Some(link_url) = link_url.clone() {
|
||||
let last_run_len = if let Some(link_url) = link_url.clone() {
|
||||
link_ranges.push(prev_len..text.len());
|
||||
link_urls.push(link_url);
|
||||
style.underline = Some(UnderlineStyle {
|
||||
thickness: 1.0.into(),
|
||||
..Default::default()
|
||||
});
|
||||
prev_len
|
||||
} else {
|
||||
// Manually scan for links
|
||||
let mut finder = linkify::LinkFinder::new();
|
||||
finder.kinds(&[linkify::LinkKind::Url]);
|
||||
let mut last_link_len = prev_len;
|
||||
for link in finder.links(&t) {
|
||||
let start = link.start();
|
||||
let end = link.end();
|
||||
let range = (prev_len + start)..(prev_len + end);
|
||||
link_ranges.push(range.clone());
|
||||
link_urls.push(link.as_str().to_string());
|
||||
|
||||
// If there is a style before we match a link, we have to add this to the highlighted ranges
|
||||
if style != HighlightStyle::default() && last_link_len < link.start() {
|
||||
highlights.push((
|
||||
last_link_len..link.start(),
|
||||
Highlight::Highlight(style),
|
||||
));
|
||||
}
|
||||
|
||||
if style != HighlightStyle::default() {
|
||||
highlights.push((
|
||||
range,
|
||||
Highlight::Highlight(HighlightStyle {
|
||||
underline: Some(UnderlineStyle {
|
||||
thickness: 1.0.into(),
|
||||
..Default::default()
|
||||
}),
|
||||
..style
|
||||
}),
|
||||
));
|
||||
|
||||
last_link_len = end;
|
||||
}
|
||||
last_link_len
|
||||
};
|
||||
|
||||
if style != HighlightStyle::default() && last_run_len < text.len() {
|
||||
let mut new_highlight = true;
|
||||
if let Some((last_range, last_style)) = highlights.last_mut() {
|
||||
if last_range.end == prev_len
|
||||
if last_range.end == last_run_len
|
||||
&& last_style == &Highlight::Highlight(style)
|
||||
{
|
||||
last_range.end = text.len();
|
||||
@ -195,7 +230,8 @@ pub fn render_markdown_mut(
|
||||
}
|
||||
}
|
||||
if new_highlight {
|
||||
highlights.push((prev_len..text.len(), Highlight::Highlight(style)));
|
||||
highlights
|
||||
.push((last_run_len..text.len(), Highlight::Highlight(style)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user