Don't need editor style to parse markdown

This commit is contained in:
Julia 2023-10-05 14:40:41 -04:00
parent a881b1f5fb
commit 8dca4c3f9a
4 changed files with 133 additions and 205 deletions

View file

@ -60,6 +60,7 @@ use itertools::Itertools;
pub use language::{char_kind, CharKind}; pub use language::{char_kind, CharKind};
use language::{ use language::{
language_settings::{self, all_language_settings, InlayHintSettings}, language_settings::{self, all_language_settings, InlayHintSettings},
markdown::MarkdownHighlight,
point_from_lsp, prepare_completion_documentation, AutoindentMode, BracketPair, Buffer, point_from_lsp, prepare_completion_documentation, AutoindentMode, BracketPair, Buffer,
CodeAction, CodeLabel, Completion, CursorShape, Diagnostic, DiagnosticSeverity, Documentation, CodeAction, CodeLabel, Completion, CursorShape, Diagnostic, DiagnosticSeverity, Documentation,
File, IndentKind, IndentSize, Language, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point, File, IndentKind, IndentSize, Language, LanguageServerName, OffsetRangeExt, OffsetUtf16, Point,
@ -120,21 +121,57 @@ pub const DOCUMENT_HIGHLIGHTS_DEBOUNCE_TIMEOUT: Duration = Duration::from_millis
pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2); pub const FORMAT_TIMEOUT: Duration = Duration::from_secs(2);
pub fn render_parsed_markdown( pub fn render_parsed_markdown(
md: &language::ParsedMarkdown, parsed: &language::ParsedMarkdown,
style: &EditorStyle, editor_style: &EditorStyle,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Text { ) -> Text {
enum RenderedMarkdown {} enum RenderedMarkdown {}
let md = md.clone(); let parsed = parsed.clone();
let code_span_background_color = style.document_highlight_read_background;
let view_id = cx.view_id(); let view_id = cx.view_id();
let code_span_background_color = editor_style.document_highlight_read_background;
let mut region_id = 0; let mut region_id = 0;
Text::new(md.text, style.text.clone())
.with_highlights(md.highlights) Text::new(parsed.text, editor_style.text.clone())
.with_custom_runs(md.region_ranges, move |ix, bounds, scene, _| { .with_highlights(
parsed
.highlights
.iter()
.filter_map(|(range, highlight)| {
let highlight = match highlight {
MarkdownHighlight::Style(style) => {
let mut highlight = HighlightStyle::default();
if style.italic {
highlight.italic = Some(true);
}
if style.underline {
highlight.underline = Some(fonts::Underline {
thickness: 1.0.into(),
..Default::default()
});
}
if style.weight != fonts::Weight::default() {
highlight.weight = Some(style.weight);
}
highlight
}
MarkdownHighlight::Code(id) => id.style(&editor_style.syntax)?,
};
Some((range.clone(), highlight))
})
.collect::<Vec<_>>(),
)
.with_custom_runs(parsed.region_ranges, move |ix, bounds, scene, _| {
region_id += 1; region_id += 1;
let region = md.regions[ix].clone(); let region = parsed.regions[ix].clone();
if let Some(url) = region.link_url { if let Some(url) = region.link_url {
scene.push_cursor_region(CursorRegion { scene.push_cursor_region(CursorRegion {
bounds, bounds,
@ -147,6 +184,7 @@ pub fn render_parsed_markdown(
}), }),
); );
} }
if region.code { if region.code {
scene.push_quad(gpui::Quad { scene.push_quad(gpui::Quad {
bounds, bounds,
@ -831,12 +869,11 @@ impl ContextMenu {
fn select_first( fn select_first(
&mut self, &mut self,
project: Option<&ModelHandle<Project>>, project: Option<&ModelHandle<Project>>,
style: theme::Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> bool { ) -> bool {
if self.visible() { if self.visible() {
match self { match self {
ContextMenu::Completions(menu) => menu.select_first(project, style, cx), ContextMenu::Completions(menu) => menu.select_first(project, cx),
ContextMenu::CodeActions(menu) => menu.select_first(cx), ContextMenu::CodeActions(menu) => menu.select_first(cx),
} }
true true
@ -848,12 +885,11 @@ impl ContextMenu {
fn select_prev( fn select_prev(
&mut self, &mut self,
project: Option<&ModelHandle<Project>>, project: Option<&ModelHandle<Project>>,
style: theme::Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> bool { ) -> bool {
if self.visible() { if self.visible() {
match self { match self {
ContextMenu::Completions(menu) => menu.select_prev(project, style, cx), ContextMenu::Completions(menu) => menu.select_prev(project, cx),
ContextMenu::CodeActions(menu) => menu.select_prev(cx), ContextMenu::CodeActions(menu) => menu.select_prev(cx),
} }
true true
@ -865,12 +901,11 @@ impl ContextMenu {
fn select_next( fn select_next(
&mut self, &mut self,
project: Option<&ModelHandle<Project>>, project: Option<&ModelHandle<Project>>,
style: theme::Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> bool { ) -> bool {
if self.visible() { if self.visible() {
match self { match self {
ContextMenu::Completions(menu) => menu.select_next(project, style, cx), ContextMenu::Completions(menu) => menu.select_next(project, cx),
ContextMenu::CodeActions(menu) => menu.select_next(cx), ContextMenu::CodeActions(menu) => menu.select_next(cx),
} }
true true
@ -882,12 +917,11 @@ impl ContextMenu {
fn select_last( fn select_last(
&mut self, &mut self,
project: Option<&ModelHandle<Project>>, project: Option<&ModelHandle<Project>>,
style: theme::Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> bool { ) -> bool {
if self.visible() { if self.visible() {
match self { match self {
ContextMenu::Completions(menu) => menu.select_last(project, style, cx), ContextMenu::Completions(menu) => menu.select_last(project, cx),
ContextMenu::CodeActions(menu) => menu.select_last(cx), ContextMenu::CodeActions(menu) => menu.select_last(cx),
} }
true true
@ -932,59 +966,54 @@ impl CompletionsMenu {
fn select_first( fn select_first(
&mut self, &mut self,
project: Option<&ModelHandle<Project>>, project: Option<&ModelHandle<Project>>,
style: theme::Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
self.selected_item = 0; self.selected_item = 0;
self.list.scroll_to(ScrollTarget::Show(self.selected_item)); self.list.scroll_to(ScrollTarget::Show(self.selected_item));
self.attempt_resolve_selected_completion(project, style, cx); self.attempt_resolve_selected_completion(project, cx);
cx.notify(); cx.notify();
} }
fn select_prev( fn select_prev(
&mut self, &mut self,
project: Option<&ModelHandle<Project>>, project: Option<&ModelHandle<Project>>,
style: theme::Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
if self.selected_item > 0 { if self.selected_item > 0 {
self.selected_item -= 1; self.selected_item -= 1;
self.list.scroll_to(ScrollTarget::Show(self.selected_item)); self.list.scroll_to(ScrollTarget::Show(self.selected_item));
} }
self.attempt_resolve_selected_completion(project, style, cx); self.attempt_resolve_selected_completion(project, cx);
cx.notify(); cx.notify();
} }
fn select_next( fn select_next(
&mut self, &mut self,
project: Option<&ModelHandle<Project>>, project: Option<&ModelHandle<Project>>,
style: theme::Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
if self.selected_item + 1 < self.matches.len() { if self.selected_item + 1 < self.matches.len() {
self.selected_item += 1; self.selected_item += 1;
self.list.scroll_to(ScrollTarget::Show(self.selected_item)); self.list.scroll_to(ScrollTarget::Show(self.selected_item));
} }
self.attempt_resolve_selected_completion(project, style, cx); self.attempt_resolve_selected_completion(project, cx);
cx.notify(); cx.notify();
} }
fn select_last( fn select_last(
&mut self, &mut self,
project: Option<&ModelHandle<Project>>, project: Option<&ModelHandle<Project>>,
style: theme::Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
self.selected_item = self.matches.len() - 1; self.selected_item = self.matches.len() - 1;
self.list.scroll_to(ScrollTarget::Show(self.selected_item)); self.list.scroll_to(ScrollTarget::Show(self.selected_item));
self.attempt_resolve_selected_completion(project, style, cx); self.attempt_resolve_selected_completion(project, cx);
cx.notify(); cx.notify();
} }
fn attempt_resolve_selected_completion( fn attempt_resolve_selected_completion(
&mut self, &mut self,
project: Option<&ModelHandle<Project>>, project: Option<&ModelHandle<Project>>,
style: theme::Editor,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
let index = self.matches[self.selected_item].candidate_id; let index = self.matches[self.selected_item].candidate_id;
@ -1029,7 +1058,6 @@ impl CompletionsMenu {
&lsp_documentation, &lsp_documentation,
&language_registry, &language_registry,
None, // TODO: Try to reasonably work out which language the completion is for None, // TODO: Try to reasonably work out which language the completion is for
&style,
) )
.await; .await;
@ -1097,10 +1125,8 @@ impl CompletionsMenu {
.as_ref() .as_ref()
.expect("It is impossible have LSP servers without a project"); .expect("It is impossible have LSP servers without a project");
let lsp_docs = lsp_docs.clone();
let lsp_docs = lsp_docs.clone(); let lsp_docs = lsp_docs.clone();
let language_registry = project.read(cx).languages().clone(); let language_registry = project.read(cx).languages().clone();
let style = style.theme.clone();
let completions = completions.clone(); let completions = completions.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
@ -1108,7 +1134,6 @@ impl CompletionsMenu {
&lsp_docs, &lsp_docs,
&language_registry, &language_registry,
None, None,
&style,
) )
.await; .await;
@ -3365,11 +3390,7 @@ impl Editor {
None None
} else { } else {
_ = this.update(&mut cx, |editor, cx| { _ = this.update(&mut cx, |editor, cx| {
menu.attempt_resolve_selected_completion( menu.attempt_resolve_selected_completion(editor.project.as_ref(), cx);
editor.project.as_ref(),
editor.style(cx).theme,
cx,
);
}); });
Some(menu) Some(menu)
} }
@ -5545,16 +5566,13 @@ impl Editor {
return; return;
} }
if self.context_menu.is_some() { if self
let style = self.style(cx).theme; .context_menu
if self .as_mut()
.context_menu .map(|menu| menu.select_last(self.project.as_ref(), cx))
.as_mut() .unwrap_or(false)
.map(|menu| menu.select_last(self.project.as_ref(), style, cx)) {
.unwrap_or(false) return;
{
return;
}
} }
if matches!(self.mode, EditorMode::SingleLine) { if matches!(self.mode, EditorMode::SingleLine) {
@ -5594,30 +5612,26 @@ impl Editor {
} }
pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) { pub fn context_menu_first(&mut self, _: &ContextMenuFirst, cx: &mut ViewContext<Self>) {
let style = self.style(cx).theme;
if let Some(context_menu) = self.context_menu.as_mut() { if let Some(context_menu) = self.context_menu.as_mut() {
context_menu.select_first(self.project.as_ref(), style, cx); context_menu.select_first(self.project.as_ref(), cx);
} }
} }
pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) { pub fn context_menu_prev(&mut self, _: &ContextMenuPrev, cx: &mut ViewContext<Self>) {
let style = self.style(cx).theme;
if let Some(context_menu) = self.context_menu.as_mut() { if let Some(context_menu) = self.context_menu.as_mut() {
context_menu.select_prev(self.project.as_ref(), style, cx); context_menu.select_prev(self.project.as_ref(), cx);
} }
} }
pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) { pub fn context_menu_next(&mut self, _: &ContextMenuNext, cx: &mut ViewContext<Self>) {
let style = self.style(cx).theme;
if let Some(context_menu) = self.context_menu.as_mut() { if let Some(context_menu) = self.context_menu.as_mut() {
context_menu.select_next(self.project.as_ref(), style, cx); context_menu.select_next(self.project.as_ref(), cx);
} }
} }
pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) { pub fn context_menu_last(&mut self, _: &ContextMenuLast, cx: &mut ViewContext<Self>) {
let style = self.style(cx).theme;
if let Some(context_menu) = self.context_menu.as_mut() { if let Some(context_menu) = self.context_menu.as_mut() {
context_menu.select_last(self.project.as_ref(), style, cx); context_menu.select_last(self.project.as_ref(), cx);
} }
} }

View file

@ -8,13 +8,11 @@ use futures::FutureExt;
use gpui::{ use gpui::{
actions, actions,
elements::{Empty, Flex, MouseEventHandler, Padding, ParentElement, Text}, elements::{Empty, Flex, MouseEventHandler, Padding, ParentElement, Text},
fonts::HighlightStyle,
platform::{CursorStyle, MouseButton}, platform::{CursorStyle, MouseButton},
AnyElement, AppContext, CursorRegion, Element, ModelHandle, MouseRegion, Task, ViewContext, AnyElement, AppContext, Element, ModelHandle, Task, ViewContext,
}; };
use language::{ use language::{
markdown::{self, ParsedRegion}, markdown, Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry, ParsedMarkdown,
Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry,
}; };
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project}; use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project};
use std::{ops::Range, sync::Arc, time::Duration}; use std::{ops::Range, sync::Arc, time::Duration};
@ -363,13 +361,11 @@ fn show_hover(
editor.hover_state.info_task = Some(task); editor.hover_state.info_task = Some(task);
} }
async fn render_blocks( async fn parse_blocks(
theme_id: usize,
blocks: &[HoverBlock], blocks: &[HoverBlock],
language_registry: &Arc<LanguageRegistry>, language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
style: &theme::Editor, ) -> markdown::ParsedMarkdown {
) -> ParsedInfo {
let mut text = String::new(); let mut text = String::new();
let mut highlights = Vec::new(); let mut highlights = Vec::new();
let mut region_ranges = Vec::new(); let mut region_ranges = Vec::new();
@ -387,7 +383,6 @@ async fn render_blocks(
&block.text, &block.text,
language_registry, language_registry,
language.clone(), language.clone(),
style,
&mut text, &mut text,
&mut highlights, &mut highlights,
&mut region_ranges, &mut region_ranges,
@ -402,13 +397,7 @@ async fn render_blocks(
.now_or_never() .now_or_never()
.and_then(Result::ok) .and_then(Result::ok)
{ {
markdown::highlight_code( markdown::highlight_code(&mut text, &mut highlights, &block.text, &language);
&mut text,
&mut highlights,
&block.text,
&language,
style,
);
} else { } else {
text.push_str(&block.text); text.push_str(&block.text);
} }
@ -416,8 +405,7 @@ async fn render_blocks(
} }
} }
ParsedInfo { ParsedMarkdown {
theme_id,
text: text.trim().to_string(), text: text.trim().to_string(),
highlights, highlights,
region_ranges, region_ranges,
@ -485,16 +473,7 @@ pub struct InfoPopover {
symbol_range: DocumentRange, symbol_range: DocumentRange,
pub blocks: Vec<HoverBlock>, pub blocks: Vec<HoverBlock>,
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
parsed_content: Option<ParsedInfo>, parsed_content: Option<ParsedMarkdown>,
}
#[derive(Debug, Clone)]
struct ParsedInfo {
theme_id: usize,
text: String,
highlights: Vec<(Range<usize>, HighlightStyle)>,
region_ranges: Vec<Range<usize>>,
regions: Vec<ParsedRegion>,
} }
impl InfoPopover { impl InfoPopover {
@ -503,58 +482,14 @@ impl InfoPopover {
style: &EditorStyle, style: &EditorStyle,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> AnyElement<Editor> { ) -> AnyElement<Editor> {
if let Some(parsed) = &self.parsed_content {
if parsed.theme_id != style.theme_id {
self.parsed_content = None;
}
}
let rendered = if let Some(parsed) = &self.parsed_content { let rendered = if let Some(parsed) = &self.parsed_content {
let view_id = cx.view_id(); crate::render_parsed_markdown(parsed, style, cx).into_any()
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 { } else {
let theme_id = style.theme_id;
let language_registry = self.project.read(cx).languages().clone(); let language_registry = self.project.read(cx).languages().clone();
let blocks = self.blocks.clone(); let blocks = self.blocks.clone();
let language = self.language.clone(); let language = self.language.clone();
let style = style.theme.clone();
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
let blocks = let blocks = parse_blocks(&blocks, &language_registry, language).await;
render_blocks(theme_id, &blocks, &language_registry, language, &style).await;
_ = this.update(&mut cx, |_, cx| cx.notify()); _ = this.update(&mut cx, |_, cx| cx.notify());
blocks blocks
}) })
@ -563,23 +498,6 @@ impl InfoPopover {
Empty::new().into_any() 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| { MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
Flex::column() Flex::column()
.scrollable::<HoverBlock>(1, None, cx) .scrollable::<HoverBlock>(1, None, cx)
@ -678,9 +596,12 @@ mod tests {
test::editor_lsp_test_context::EditorLspTestContext, test::editor_lsp_test_context::EditorLspTestContext,
}; };
use collections::BTreeSet; use collections::BTreeSet;
use gpui::fonts::{Underline, Weight}; use gpui::fonts::Weight;
use indoc::indoc; use indoc::indoc;
use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet}; use language::{
language_settings::InlayHintSettings, markdown::MarkdownHighlightStyle, Diagnostic,
DiagnosticSet,
};
use lsp::LanguageServerId; use lsp::LanguageServerId;
use project::{HoverBlock, HoverBlockKind}; use project::{HoverBlock, HoverBlockKind};
use smol::stream::StreamExt; use smol::stream::StreamExt;
@ -893,7 +814,7 @@ mod tests {
.await; .await;
cx.condition(|editor, _| editor.hover_state.visible()).await; cx.condition(|editor, _| editor.hover_state.visible()).await;
cx.editor(|editor, cx| { cx.editor(|editor, _| {
let blocks = editor.hover_state.info_popover.clone().unwrap().blocks; let blocks = editor.hover_state.info_popover.clone().unwrap().blocks;
assert_eq!( assert_eq!(
blocks, blocks,
@ -903,9 +824,7 @@ mod tests {
}], }],
); );
let style = editor.style(cx); let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
let rendered =
smol::block_on(render_blocks(0, &blocks, &Default::default(), None, &style));
assert_eq!( assert_eq!(
rendered.text, rendered.text,
code_str.trim(), code_str.trim(),
@ -984,16 +903,17 @@ mod tests {
#[gpui::test] #[gpui::test]
fn test_render_blocks(cx: &mut gpui::TestAppContext) { fn test_render_blocks(cx: &mut gpui::TestAppContext) {
use markdown::MarkdownHighlight;
init_test(cx, |_| {}); init_test(cx, |_| {});
cx.add_window(|cx| { cx.add_window(|cx| {
let editor = Editor::single_line(None, cx); let editor = Editor::single_line(None, cx);
let style = editor.style(cx);
struct Row { struct Row {
blocks: Vec<HoverBlock>, blocks: Vec<HoverBlock>,
expected_marked_text: String, expected_marked_text: String,
expected_styles: Vec<HighlightStyle>, expected_styles: Vec<MarkdownHighlight>,
} }
let rows = &[ let rows = &[
@ -1004,10 +924,10 @@ mod tests {
kind: HoverBlockKind::Markdown, kind: HoverBlockKind::Markdown,
}], }],
expected_marked_text: "one «two» three".to_string(), expected_marked_text: "one «two» three".to_string(),
expected_styles: vec![HighlightStyle { expected_styles: vec![MarkdownHighlight::Style(MarkdownHighlightStyle {
weight: Some(Weight::BOLD), weight: Weight::BOLD,
..Default::default() ..Default::default()
}], })],
}, },
// Links // Links
Row { Row {
@ -1016,13 +936,10 @@ mod tests {
kind: HoverBlockKind::Markdown, kind: HoverBlockKind::Markdown,
}], }],
expected_marked_text: "one «two» three".to_string(), expected_marked_text: "one «two» three".to_string(),
expected_styles: vec![HighlightStyle { expected_styles: vec![MarkdownHighlight::Style(MarkdownHighlightStyle {
underline: Some(Underline { underline: true,
thickness: 1.0.into(),
..Default::default()
}),
..Default::default() ..Default::default()
}], })],
}, },
// Lists // Lists
Row { Row {
@ -1047,13 +964,10 @@ mod tests {
- «c» - «c»
- d" - d"
.unindent(), .unindent(),
expected_styles: vec![HighlightStyle { expected_styles: vec![MarkdownHighlight::Style(MarkdownHighlightStyle {
underline: Some(Underline { underline: true,
thickness: 1.0.into(),
..Default::default()
}),
..Default::default() ..Default::default()
}], })],
}, },
// Multi-paragraph list items // Multi-paragraph list items
Row { Row {
@ -1081,13 +995,10 @@ mod tests {
- ten - ten
- six" - six"
.unindent(), .unindent(),
expected_styles: vec![HighlightStyle { expected_styles: vec![MarkdownHighlight::Style(MarkdownHighlightStyle {
underline: Some(Underline { underline: true,
thickness: 1.0.into(),
..Default::default()
}),
..Default::default() ..Default::default()
}], })],
}, },
]; ];
@ -1097,8 +1008,7 @@ mod tests {
expected_styles, expected_styles,
} in &rows[0..] } in &rows[0..]
{ {
let rendered = let rendered = smol::block_on(parse_blocks(&blocks, &Default::default(), None));
smol::block_on(render_blocks(0, &blocks, &Default::default(), None, &style));
let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false); let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
let expected_highlights = ranges let expected_highlights = ranges

View file

@ -149,7 +149,6 @@ pub async fn prepare_completion_documentation(
documentation: &lsp::Documentation, documentation: &lsp::Documentation,
language_registry: &Arc<LanguageRegistry>, language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
style: &theme::Editor,
) -> Option<Documentation> { ) -> Option<Documentation> {
match documentation { match documentation {
lsp::Documentation::String(text) => { lsp::Documentation::String(text) => {
@ -170,7 +169,7 @@ pub async fn prepare_completion_documentation(
} }
lsp::MarkupKind::Markdown => { lsp::MarkupKind::Markdown => {
let parsed = parse_markdown(value, language_registry, language, style).await; let parsed = parse_markdown(value, language_registry, language).await;
Some(Documentation::MultiLineMarkdown(parsed)) Some(Documentation::MultiLineMarkdown(parsed))
} }
}, },

View file

@ -1,18 +1,31 @@
use std::ops::Range; use std::ops::Range;
use std::sync::Arc; use std::sync::Arc;
use crate::{Language, LanguageRegistry}; use crate::{HighlightId, Language, LanguageRegistry};
use gpui::fonts::{HighlightStyle, Underline, Weight}; use gpui::fonts::Weight;
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag}; use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ParsedMarkdown { pub struct ParsedMarkdown {
pub text: String, pub text: String,
pub highlights: Vec<(Range<usize>, HighlightStyle)>, pub highlights: Vec<(Range<usize>, MarkdownHighlight)>,
pub region_ranges: Vec<Range<usize>>, pub region_ranges: Vec<Range<usize>>,
pub regions: Vec<ParsedRegion>, pub regions: Vec<ParsedRegion>,
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MarkdownHighlight {
Style(MarkdownHighlightStyle),
Code(HighlightId),
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct MarkdownHighlightStyle {
pub italic: bool,
pub underline: bool,
pub weight: Weight,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ParsedRegion { pub struct ParsedRegion {
pub code: bool, pub code: bool,
@ -23,7 +36,6 @@ pub async fn parse_markdown(
markdown: &str, markdown: &str,
language_registry: &Arc<LanguageRegistry>, language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
style: &theme::Editor,
) -> ParsedMarkdown { ) -> ParsedMarkdown {
let mut text = String::new(); let mut text = String::new();
let mut highlights = Vec::new(); let mut highlights = Vec::new();
@ -34,7 +46,6 @@ pub async fn parse_markdown(
markdown, markdown,
language_registry, language_registry,
language, language,
style,
&mut text, &mut text,
&mut highlights, &mut highlights,
&mut region_ranges, &mut region_ranges,
@ -54,9 +65,8 @@ pub async fn parse_markdown_block(
markdown: &str, markdown: &str,
language_registry: &Arc<LanguageRegistry>, language_registry: &Arc<LanguageRegistry>,
language: Option<Arc<Language>>, language: Option<Arc<Language>>,
style: &theme::Editor,
text: &mut String, text: &mut String,
highlights: &mut Vec<(Range<usize>, HighlightStyle)>, highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
region_ranges: &mut Vec<Range<usize>>, region_ranges: &mut Vec<Range<usize>>,
regions: &mut Vec<ParsedRegion>, regions: &mut Vec<ParsedRegion>,
) { ) {
@ -71,16 +81,16 @@ pub async fn parse_markdown_block(
match event { match event {
Event::Text(t) => { Event::Text(t) => {
if let Some(language) = &current_language { if let Some(language) = &current_language {
highlight_code(text, highlights, t.as_ref(), language, style); highlight_code(text, highlights, t.as_ref(), language);
} else { } else {
text.push_str(t.as_ref()); text.push_str(t.as_ref());
let mut style = HighlightStyle::default(); let mut style = MarkdownHighlightStyle::default();
if bold_depth > 0 { if bold_depth > 0 {
style.weight = Some(Weight::BOLD); style.weight = Weight::BOLD;
} }
if italic_depth > 0 { if italic_depth > 0 {
style.italic = Some(true); style.italic = true;
} }
if let Some(link_url) = link_url.clone() { if let Some(link_url) = link_url.clone() {
region_ranges.push(prev_len..text.len()); region_ranges.push(prev_len..text.len());
@ -88,22 +98,22 @@ pub async fn parse_markdown_block(
link_url: Some(link_url), link_url: Some(link_url),
code: false, code: false,
}); });
style.underline = Some(Underline { style.underline = true;
thickness: 1.0.into(),
..Default::default()
});
} }
if style != HighlightStyle::default() { if style != MarkdownHighlightStyle::default() {
let mut new_highlight = true; let mut new_highlight = true;
if let Some((last_range, last_style)) = highlights.last_mut() { if let Some((last_range, MarkdownHighlight::Style(last_style))) =
highlights.last_mut()
{
if last_range.end == prev_len && last_style == &style { if last_range.end == prev_len && last_style == &style {
last_range.end = text.len(); last_range.end = text.len();
new_highlight = false; new_highlight = false;
} }
} }
if new_highlight { if new_highlight {
highlights.push((prev_len..text.len(), style)); let range = prev_len..text.len();
highlights.push((range, MarkdownHighlight::Style(style)));
} }
} }
} }
@ -115,13 +125,10 @@ pub async fn parse_markdown_block(
if link_url.is_some() { if link_url.is_some() {
highlights.push(( highlights.push((
prev_len..text.len(), prev_len..text.len(),
HighlightStyle { MarkdownHighlight::Style(MarkdownHighlightStyle {
underline: Some(Underline { underline: true,
thickness: 1.0.into(),
..Default::default()
}),
..Default::default() ..Default::default()
}, }),
)); ));
} }
regions.push(ParsedRegion { regions.push(ParsedRegion {
@ -204,17 +211,15 @@ pub async fn parse_markdown_block(
pub fn highlight_code( pub fn highlight_code(
text: &mut String, text: &mut String,
highlights: &mut Vec<(Range<usize>, HighlightStyle)>, highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
content: &str, content: &str,
language: &Arc<Language>, language: &Arc<Language>,
style: &theme::Editor,
) { ) {
let prev_len = text.len(); let prev_len = text.len();
text.push_str(content); text.push_str(content);
for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) { for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) {
if let Some(style) = highlight_id.style(&style.syntax) { let highlight = MarkdownHighlight::Code(highlight_id);
highlights.push((prev_len + range.start..prev_len + range.end, style)); highlights.push((prev_len + range.start..prev_len + range.end, highlight));
}
} }
} }