Selectable diagnostic popover text (#14518)

Release Notes:

- Added the ability to select and copy text from diagnostic popovers

- Fixed #12695




https://github.com/user-attachments/assets/b896bacf-ecbc-48f5-8228-a3821f0b1a4e

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Ephram 2024-07-29 01:13:13 -04:00 committed by GitHub
parent 583b6235fb
commit 78a2539d59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 206 additions and 84 deletions

View file

@ -10,7 +10,7 @@ use gpui::{
TextStyle, TextStyleRefinement, View,
};
use language::{Language, LanguageRegistry, Rope};
use parser::{parse_markdown, MarkdownEvent, MarkdownTag, MarkdownTagEnd};
use parser::{parse_links_only, parse_markdown, MarkdownEvent, MarkdownTag, MarkdownTagEnd};
use std::{iter, mem, ops::Range, rc::Rc, sync::Arc};
use theme::SyntaxTheme;
@ -61,6 +61,7 @@ pub struct Markdown {
focus_handle: FocusHandle,
language_registry: Option<Arc<LanguageRegistry>>,
fallback_code_block_language: Option<String>,
parse_links_only: bool,
}
actions!(markdown, [Copy]);
@ -86,6 +87,33 @@ impl Markdown {
focus_handle,
language_registry,
fallback_code_block_language,
parse_links_only: false,
};
this.parse(cx);
this
}
pub fn new_text(
source: String,
style: MarkdownStyle,
language_registry: Option<Arc<LanguageRegistry>>,
cx: &mut ViewContext<Self>,
fallback_code_block_language: Option<String>,
) -> Self {
let focus_handle = cx.focus_handle();
let mut this = Self {
source,
selection: Selection::default(),
pressed_link: None,
autoscroll_request: None,
style,
should_reparse: false,
parsed_markdown: ParsedMarkdown::default(),
pending_parse: None,
focus_handle,
language_registry,
fallback_code_block_language,
parse_links_only: true,
};
this.parse(cx);
this
@ -136,9 +164,13 @@ impl Markdown {
}
let text = self.source.clone();
let parse_text_only = self.parse_links_only;
let parsed = cx.background_executor().spawn(async move {
let text = SharedString::from(text);
let events = Arc::from(parse_markdown(text.as_ref()));
let events = match parse_text_only {
true => Arc::from(parse_links_only(text.as_ref())),
false => Arc::from(parse_markdown(text.as_ref())),
};
anyhow::Ok(ParsedMarkdown {
source: text,
events,

View file

@ -88,6 +88,41 @@ pub fn parse_markdown(text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
events
}
pub fn parse_links_only(text: &str) -> Vec<(Range<usize>, MarkdownEvent)> {
let mut events = Vec::new();
let mut finder = LinkFinder::new();
finder.kinds(&[linkify::LinkKind::Url]);
let mut text_range = Range {
start: 0,
end: text.len(),
};
for link in finder.links(&text[text_range.clone()]) {
let link_range = text_range.start + link.start()..text_range.start + link.end();
if link_range.start > text_range.start {
events.push((text_range.start..link_range.start, MarkdownEvent::Text));
}
events.push((
link_range.clone(),
MarkdownEvent::Start(MarkdownTag::Link {
link_type: LinkType::Autolink,
dest_url: SharedString::from(link.as_str().to_string()),
title: SharedString::default(),
id: SharedString::default(),
}),
));
events.push((link_range.clone(), MarkdownEvent::Text));
events.push((link_range.clone(), MarkdownEvent::End(MarkdownTagEnd::Link)));
text_range.start = link_range.end;
}
events.push((text_range, MarkdownEvent::Text));
events
}
/// A static-lifetime equivalent of pulldown_cmark::Event so we can cache the
/// parse result for rendering without resorting to unsafe lifetime coercion.
#[derive(Clone, Debug, PartialEq)]