mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-24 17:28:40 +00:00
Wait for language to load when parsing markdown
This commit is contained in:
parent
ea6f366d23
commit
a881b1f5fb
4 changed files with 137 additions and 92 deletions
|
@ -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, _>(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue