Wait for language to load when parsing markdown

This commit is contained in:
Julia 2023-10-04 17:36:51 -04:00
parent ea6f366d23
commit a881b1f5fb
4 changed files with 137 additions and 92 deletions

View file

@ -1018,7 +1018,6 @@ impl CompletionsMenu {
return;
}
// TODO: Do on background
cx.spawn(|this, mut cx| async move {
let request = server.request::<lsp::request::ResolveCompletionItem>(completion);
let Some(completion_item) = request.await.log_err() else {
@ -1031,7 +1030,8 @@ impl CompletionsMenu {
&language_registry,
None, // TODO: Try to reasonably work out which language the completion is for
&style,
);
)
.await;
let mut completions = completions.write();
let completion = &mut completions[index];
@ -1084,30 +1084,46 @@ impl CompletionsMenu {
let style = style.clone();
move |_, range, items, cx| {
let start_ix = range.start;
let mut completions = completions.write();
let completions_guard = completions.read();
for (ix, mat) in matches[range].iter().enumerate() {
let completion = &mut completions[mat.candidate_id];
let item_ix = start_ix + ix;
let candidate_id = mat.candidate_id;
let completion = &completions_guard[candidate_id];
if completion.documentation.is_none() {
if item_ix == selected_item && completion.documentation.is_none() {
if let Some(lsp_docs) = &completion.lsp_completion.documentation {
let project = project
.as_ref()
.expect("It is impossible have LSP servers without a project");
let language_registry = project.read(cx).languages();
let lsp_docs = lsp_docs.clone();
let lsp_docs = lsp_docs.clone();
let language_registry = project.read(cx).languages().clone();
let style = style.theme.clone();
let completions = completions.clone();
completion.documentation = prepare_completion_documentation(
lsp_docs,
language_registry,
None,
&style.theme,
);
cx.spawn(|this, mut cx| async move {
let documentation = prepare_completion_documentation(
&lsp_docs,
&language_registry,
None,
&style,
)
.await;
this.update(&mut cx, |_, cx| {
let mut completions = completions.write();
completions[candidate_id].documentation = documentation;
drop(completions);
cx.notify();
})
})
.detach();
}
}
let documentation = &completion.documentation;
let item_ix = start_ix + ix;
items.push(
MouseEventHandler::new::<CompletionTag, _>(

View file

@ -7,7 +7,7 @@ use crate::{
use futures::FutureExt;
use gpui::{
actions,
elements::{Flex, MouseEventHandler, Padding, ParentElement, Text},
elements::{Empty, Flex, MouseEventHandler, Padding, ParentElement, Text},
fonts::HighlightStyle,
platform::{CursorStyle, MouseButton},
AnyElement, AppContext, CursorRegion, Element, ModelHandle, MouseRegion, Task, ViewContext,
@ -128,7 +128,7 @@ pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut Vie
symbol_range: DocumentRange::Inlay(inlay_hover.range),
blocks: vec![inlay_hover.tooltip],
language: None,
rendered_content: None,
parsed_content: None,
};
this.update(&mut cx, |this, cx| {
@ -332,7 +332,7 @@ fn show_hover(
symbol_range: DocumentRange::Text(range),
blocks: hover_result.contents,
language: hover_result.language,
rendered_content: None,
parsed_content: None,
})
});
@ -363,12 +363,12 @@ fn show_hover(
editor.hover_state.info_task = Some(task);
}
fn render_blocks(
async fn render_blocks(
theme_id: usize,
blocks: &[HoverBlock],
language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>,
style: &EditorStyle,
style: &theme::Editor,
) -> ParsedInfo {
let mut text = String::new();
let mut highlights = Vec::new();
@ -382,16 +382,19 @@ fn render_blocks(
text.push_str(&block.text);
}
HoverBlockKind::Markdown => markdown::parse_markdown_block(
&block.text,
language_registry,
language.clone(),
style,
&mut text,
&mut highlights,
&mut region_ranges,
&mut regions,
),
HoverBlockKind::Markdown => {
markdown::parse_markdown_block(
&block.text,
language_registry,
language.clone(),
style,
&mut text,
&mut highlights,
&mut region_ranges,
&mut regions,
)
.await
}
HoverBlockKind::Code { language } => {
if let Some(language) = language_registry
@ -482,7 +485,7 @@ pub struct InfoPopover {
symbol_range: DocumentRange,
pub blocks: Vec<HoverBlock>,
language: Option<Arc<Language>>,
rendered_content: Option<ParsedInfo>,
parsed_content: Option<ParsedInfo>,
}
#[derive(Debug, Clone)]
@ -500,63 +503,87 @@ impl InfoPopover {
style: &EditorStyle,
cx: &mut ViewContext<Editor>,
) -> AnyElement<Editor> {
if let Some(rendered) = &self.rendered_content {
if rendered.theme_id != style.theme_id {
self.rendered_content = None;
if let Some(parsed) = &self.parsed_content {
if parsed.theme_id != style.theme_id {
self.parsed_content = None;
}
}
let rendered_content = self.rendered_content.get_or_insert_with(|| {
render_blocks(
style.theme_id,
&self.blocks,
self.project.read(cx).languages(),
self.language.clone(),
style,
)
});
let rendered = if let Some(parsed) = &self.parsed_content {
let view_id = cx.view_id();
let regions = parsed.regions.clone();
let code_span_background_color = style.document_highlight_read_background;
let mut region_id = 0;
Text::new(parsed.text.clone(), style.text.clone())
.with_highlights(parsed.highlights.clone())
.with_custom_runs(parsed.region_ranges.clone(), move |ix, bounds, scene, _| {
region_id += 1;
let region = regions[ix].clone();
if let Some(url) = region.link_url {
scene.push_cursor_region(CursorRegion {
bounds,
style: CursorStyle::PointingHand,
});
scene.push_mouse_region(
MouseRegion::new::<Self>(view_id, region_id, bounds)
.on_click::<Editor, _>(MouseButton::Left, move |_, _, cx| {
cx.platform().open_url(&url)
}),
);
}
if region.code {
scene.push_quad(gpui::Quad {
bounds,
background: Some(code_span_background_color),
border: Default::default(),
corner_radii: (2.0).into(),
});
}
})
.with_soft_wrap(true)
.into_any()
} else {
let theme_id = style.theme_id;
let language_registry = self.project.read(cx).languages().clone();
let blocks = self.blocks.clone();
let language = self.language.clone();
let style = style.theme.clone();
cx.spawn(|this, mut cx| async move {
let blocks =
render_blocks(theme_id, &blocks, &language_registry, language, &style).await;
_ = this.update(&mut cx, |_, cx| cx.notify());
blocks
})
.detach();
Empty::new().into_any()
};
// let rendered_content = self.parsed_content.get_or_insert_with(|| {
// let language_registry = self.project.read(cx).languages().clone();
// cx.spawn(|this, mut cx| async move {
// let blocks = render_blocks(
// style.theme_id,
// &self.blocks,
// &language_registry,
// self.language.clone(),
// style,
// )
// .await;
// this.update(&mut cx, |_, cx| cx.notify());
// blocks
// })
// .shared()
// });
MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
let mut region_id = 0;
let view_id = cx.view_id();
let code_span_background_color = style.document_highlight_read_background;
let regions = rendered_content.regions.clone();
Flex::column()
.scrollable::<HoverBlock>(1, None, cx)
.with_child(
Text::new(rendered_content.text.clone(), style.text.clone())
.with_highlights(rendered_content.highlights.clone())
.with_custom_runs(
rendered_content.region_ranges.clone(),
move |ix, bounds, scene, _| {
region_id += 1;
let region = regions[ix].clone();
if let Some(url) = region.link_url {
scene.push_cursor_region(CursorRegion {
bounds,
style: CursorStyle::PointingHand,
});
scene.push_mouse_region(
MouseRegion::new::<Self>(view_id, region_id, bounds)
.on_click::<Editor, _>(
MouseButton::Left,
move |_, _, cx| cx.platform().open_url(&url),
),
);
}
if region.code {
scene.push_quad(gpui::Quad {
bounds,
background: Some(code_span_background_color),
border: Default::default(),
corner_radii: (2.0).into(),
});
}
},
)
.with_soft_wrap(true),
)
.with_child(rendered)
.contained()
.with_style(style.hover_popover.container)
})
@ -877,7 +904,8 @@ mod tests {
);
let style = editor.style(cx);
let rendered = render_blocks(0, &blocks, &Default::default(), None, &style);
let rendered =
smol::block_on(render_blocks(0, &blocks, &Default::default(), None, &style));
assert_eq!(
rendered.text,
code_str.trim(),
@ -1069,7 +1097,8 @@ mod tests {
expected_styles,
} in &rows[0..]
{
let rendered = render_blocks(0, &blocks, &Default::default(), None, &style);
let rendered =
smol::block_on(render_blocks(0, &blocks, &Default::default(), None, &style));
let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
let expected_highlights = ranges
@ -1339,7 +1368,7 @@ mod tests {
);
assert_eq!(
popover
.rendered_content
.parsed_content
.as_ref()
.expect("should have label text for new type hint")
.text,
@ -1403,7 +1432,7 @@ mod tests {
);
assert_eq!(
popover
.rendered_content
.parsed_content
.as_ref()
.expect("should have label text for struct hint")
.text,

View file

@ -7,7 +7,7 @@ pub use crate::{
use crate::{
diagnostic_set::{DiagnosticEntry, DiagnosticGroup},
language_settings::{language_settings, LanguageSettings},
markdown,
markdown::parse_markdown,
outline::OutlineItem,
syntax_map::{
SyntaxLayerInfo, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatches,
@ -145,7 +145,7 @@ pub struct Diagnostic {
pub is_unnecessary: bool,
}
pub fn prepare_completion_documentation(
pub async fn prepare_completion_documentation(
documentation: &lsp::Documentation,
language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>,
@ -170,7 +170,7 @@ pub fn prepare_completion_documentation(
}
lsp::MarkupKind::Markdown => {
let parsed = markdown::parse_markdown(value, language_registry, language, style);
let parsed = parse_markdown(value, language_registry, language, style).await;
Some(Documentation::MultiLineMarkdown(parsed))
}
},

View file

@ -2,7 +2,6 @@ use std::ops::Range;
use std::sync::Arc;
use crate::{Language, LanguageRegistry};
use futures::FutureExt;
use gpui::fonts::{HighlightStyle, Underline, Weight};
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
@ -20,7 +19,7 @@ pub struct ParsedRegion {
pub link_url: Option<String>,
}
pub fn parse_markdown(
pub async fn parse_markdown(
markdown: &str,
language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>,
@ -40,7 +39,8 @@ pub fn parse_markdown(
&mut highlights,
&mut region_ranges,
&mut regions,
);
)
.await;
ParsedMarkdown {
text,
@ -50,7 +50,7 @@ pub fn parse_markdown(
}
}
pub fn parse_markdown_block(
pub async fn parse_markdown_block(
markdown: &str,
language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>,
@ -143,8 +143,8 @@ pub fn parse_markdown_block(
current_language = if let CodeBlockKind::Fenced(language) = kind {
language_registry
.language_for_name(language.as_ref())
.now_or_never()
.and_then(Result::ok)
.await
.ok()
} else {
language.clone()
}