Detailed changes
@@ -1558,12 +1558,12 @@ dependencies = [
"gpui",
"language",
"log",
- "markdown_element",
"menu",
"picker",
"postage",
"project",
"recent_projects",
+ "rich_text",
"schemars",
"serde",
"serde_derive",
@@ -2406,6 +2406,7 @@ dependencies = [
"project",
"pulldown-cmark",
"rand 0.8.5",
+ "rich_text",
"rpc",
"schemars",
"serde",
@@ -4324,24 +4325,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "markdown_element"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "collections",
- "futures 0.3.28",
- "gpui",
- "language",
- "lazy_static",
- "pulldown-cmark",
- "smallvec",
- "smol",
- "sum_tree",
- "theme",
- "util",
-]
-
[[package]]
name = "matchers"
version = "0.1.0"
@@ -6261,6 +6244,24 @@ dependencies = [
"bytemuck",
]
+[[package]]
+name = "rich_text"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "collections",
+ "futures 0.3.28",
+ "gpui",
+ "language",
+ "lazy_static",
+ "pulldown-cmark",
+ "smallvec",
+ "smol",
+ "sum_tree",
+ "theme",
+ "util",
+]
+
[[package]]
name = "ring"
version = "0.16.20"
@@ -46,7 +46,6 @@ members = [
"crates/lsp",
"crates/media",
"crates/menu",
- "crates/markdown_element",
"crates/node_runtime",
"crates/outline",
"crates/picker",
@@ -65,6 +64,7 @@ members = [
"crates/sqlez",
"crates/sqlez_macros",
"crates/feature_flags",
+ "crates/rich_text",
"crates/storybook",
"crates/sum_tree",
"crates/terminal",
@@ -37,7 +37,7 @@ fuzzy = { path = "../fuzzy" }
gpui = { path = "../gpui" }
language = { path = "../language" }
menu = { path = "../menu" }
-markdown_element = { path = "../markdown_element" }
+rich_text = { path = "../rich_text" }
picker = { path = "../picker" }
project = { path = "../project" }
recent_projects = {path = "../recent_projects"}
@@ -17,9 +17,9 @@ use gpui::{
View, ViewContext, ViewHandle, WeakViewHandle,
};
use language::{language_settings::SoftWrap, LanguageRegistry};
-use markdown_element::{MarkdownData, MarkdownElement};
use menu::Confirm;
use project::Fs;
+use rich_text::RichText;
use serde::{Deserialize, Serialize};
use settings::SettingsStore;
use std::sync::Arc;
@@ -50,7 +50,7 @@ pub struct ChatPanel {
subscriptions: Vec<gpui::Subscription>,
workspace: WeakViewHandle<Workspace>,
has_focus: bool,
- markdown_data: HashMap<ChannelMessageId, Arc<MarkdownData>>,
+ markdown_data: HashMap<ChannelMessageId, RichText>,
}
#[derive(Serialize, Deserialize)]
@@ -370,12 +370,10 @@ impl ChatPanel {
};
let is_pending = message.is_pending();
- let markdown = self.markdown_data.entry(message.id).or_insert_with(|| {
- Arc::new(markdown_element::render_markdown(
- message.body,
- &self.languages,
- ))
- });
+ let text = self
+ .markdown_data
+ .entry(message.id)
+ .or_insert_with(|| rich_text::render_markdown(message.body, &self.languages, None));
let now = OffsetDateTime::now_utc();
let theme = theme::current(cx);
@@ -401,11 +399,11 @@ impl ChatPanel {
if is_continuation {
Flex::row()
.with_child(
- MarkdownElement::new(
- markdown.clone(),
- style.body.clone(),
+ text.element(
theme.editor.syntax.clone(),
+ style.body.clone(),
theme.editor.document_highlight_read_background,
+ cx,
)
.flex(1., true),
)
@@ -457,11 +455,11 @@ impl ChatPanel {
.with_child(
Flex::row()
.with_child(
- MarkdownElement::new(
- markdown.clone(),
- style.body.clone(),
+ text.element(
theme.editor.syntax.clone(),
+ style.body.clone(),
theme.editor.document_highlight_read_background,
+ cx,
)
.flex(1., true),
)
@@ -36,6 +36,7 @@ language = { path = "../language" }
lsp = { path = "../lsp" }
project = { path = "../project" }
rpc = { path = "../rpc" }
+rich_text = { path = "../rich_text" }
settings = { path = "../settings" }
snippet = { path = "../snippet" }
sum_tree = { path = "../sum_tree" }
@@ -8,12 +8,12 @@ use futures::FutureExt;
use gpui::{
actions,
elements::{Flex, MouseEventHandler, Padding, ParentElement, Text},
- fonts::{HighlightStyle, Underline, Weight},
platform::{CursorStyle, MouseButton},
- AnyElement, AppContext, CursorRegion, Element, ModelHandle, MouseRegion, Task, ViewContext,
+ AnyElement, AppContext, Element, ModelHandle, Task, ViewContext,
};
use language::{Bias, DiagnosticEntry, DiagnosticSeverity, Language, LanguageRegistry};
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart, Project};
+use rich_text::{new_paragraph, render_code, render_markdown_mut, RichText};
use std::{ops::Range, sync::Arc, time::Duration};
use util::TryFutureExt;
@@ -346,158 +346,25 @@ fn show_hover(
}
fn render_blocks(
- theme_id: usize,
blocks: &[HoverBlock],
language_registry: &Arc<LanguageRegistry>,
language: Option<&Arc<Language>>,
- style: &EditorStyle,
-) -> RenderedInfo {
- let mut text = String::new();
- let mut highlights = Vec::new();
- let mut region_ranges = Vec::new();
- let mut regions = Vec::new();
+) -> RichText {
+ let mut data = RichText {
+ text: Default::default(),
+ highlights: Default::default(),
+ region_ranges: Default::default(),
+ regions: Default::default(),
+ };
for block in blocks {
match &block.kind {
HoverBlockKind::PlainText => {
- new_paragraph(&mut text, &mut Vec::new());
- text.push_str(&block.text);
+ new_paragraph(&mut data.text, &mut Vec::new());
+ data.text.push_str(&block.text);
}
HoverBlockKind::Markdown => {
- use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
-
- let mut bold_depth = 0;
- let mut italic_depth = 0;
- let mut link_url = None;
- let mut current_language = None;
- let mut list_stack = Vec::new();
-
- for event in Parser::new_ext(&block.text, Options::all()) {
- let prev_len = text.len();
- match event {
- Event::Text(t) => {
- if let Some(language) = ¤t_language {
- render_code(
- &mut text,
- &mut highlights,
- t.as_ref(),
- language,
- style,
- );
- } else {
- text.push_str(t.as_ref());
-
- let mut style = HighlightStyle::default();
- if bold_depth > 0 {
- style.weight = Some(Weight::BOLD);
- }
- if italic_depth > 0 {
- style.italic = Some(true);
- }
- if let Some(link_url) = link_url.clone() {
- region_ranges.push(prev_len..text.len());
- regions.push(RenderedRegion {
- link_url: Some(link_url),
- code: false,
- });
- style.underline = Some(Underline {
- thickness: 1.0.into(),
- ..Default::default()
- });
- }
-
- if style != HighlightStyle::default() {
- let mut new_highlight = true;
- if let Some((last_range, last_style)) = highlights.last_mut() {
- if last_range.end == prev_len && last_style == &style {
- last_range.end = text.len();
- new_highlight = false;
- }
- }
- if new_highlight {
- highlights.push((prev_len..text.len(), style));
- }
- }
- }
- }
- Event::Code(t) => {
- text.push_str(t.as_ref());
- region_ranges.push(prev_len..text.len());
- if link_url.is_some() {
- highlights.push((
- prev_len..text.len(),
- HighlightStyle {
- underline: Some(Underline {
- thickness: 1.0.into(),
- ..Default::default()
- }),
- ..Default::default()
- },
- ));
- }
- regions.push(RenderedRegion {
- code: true,
- link_url: link_url.clone(),
- });
- }
- Event::Start(tag) => match tag {
- Tag::Paragraph => new_paragraph(&mut text, &mut list_stack),
- Tag::Heading(_, _, _) => {
- new_paragraph(&mut text, &mut list_stack);
- bold_depth += 1;
- }
- Tag::CodeBlock(kind) => {
- new_paragraph(&mut text, &mut list_stack);
- current_language = if let CodeBlockKind::Fenced(language) = kind {
- language_registry
- .language_for_name(language.as_ref())
- .now_or_never()
- .and_then(Result::ok)
- } else {
- language.cloned()
- }
- }
- Tag::Emphasis => italic_depth += 1,
- Tag::Strong => bold_depth += 1,
- Tag::Link(_, url, _) => link_url = Some(url.to_string()),
- Tag::List(number) => {
- list_stack.push((number, false));
- }
- Tag::Item => {
- let len = list_stack.len();
- if let Some((list_number, has_content)) = list_stack.last_mut() {
- *has_content = false;
- if !text.is_empty() && !text.ends_with('\n') {
- text.push('\n');
- }
- for _ in 0..len - 1 {
- text.push_str(" ");
- }
- if let Some(number) = list_number {
- text.push_str(&format!("{}. ", number));
- *number += 1;
- *has_content = false;
- } else {
- text.push_str("- ");
- }
- }
- }
- _ => {}
- },
- Event::End(tag) => match tag {
- Tag::Heading(_, _, _) => bold_depth -= 1,
- Tag::CodeBlock(_) => current_language = None,
- Tag::Emphasis => italic_depth -= 1,
- Tag::Strong => bold_depth -= 1,
- Tag::Link(_, _, _) => link_url = None,
- Tag::List(_) => drop(list_stack.pop()),
- _ => {}
- },
- Event::HardBreak => text.push('\n'),
- Event::SoftBreak => text.push(' '),
- _ => {}
- }
- }
+ render_markdown_mut(&block.text, language_registry, language, &mut data)
}
HoverBlockKind::Code { language } => {
if let Some(language) = language_registry
@@ -505,62 +372,17 @@ fn render_blocks(
.now_or_never()
.and_then(Result::ok)
{
- render_code(&mut text, &mut highlights, &block.text, &language, style);
+ render_code(&mut data.text, &mut data.highlights, &block.text, &language);
} else {
- text.push_str(&block.text);
+ data.text.push_str(&block.text);
}
}
}
}
- RenderedInfo {
- theme_id,
- text: text.trim().to_string(),
- highlights,
- region_ranges,
- regions,
- }
-}
-
-fn render_code(
- text: &mut String,
- highlights: &mut Vec<(Range<usize>, HighlightStyle)>,
- content: &str,
- language: &Arc<Language>,
- style: &EditorStyle,
-) {
- let prev_len = text.len();
- text.push_str(content);
- for (range, highlight_id) in language.highlight_text(&content.into(), 0..content.len()) {
- if let Some(style) = highlight_id.style(&style.syntax) {
- highlights.push((prev_len + range.start..prev_len + range.end, style));
- }
- }
-}
-
-fn new_paragraph(text: &mut String, list_stack: &mut Vec<(Option<u64>, bool)>) {
- let mut is_subsequent_paragraph_of_list = false;
- if let Some((_, has_content)) = list_stack.last_mut() {
- if *has_content {
- is_subsequent_paragraph_of_list = true;
- } else {
- *has_content = true;
- return;
- }
- }
+ data.text = data.text.trim().to_string();
- if !text.is_empty() {
- if !text.ends_with('\n') {
- text.push('\n');
- }
- text.push('\n');
- }
- for _ in 0..list_stack.len().saturating_sub(1) {
- text.push_str(" ");
- }
- if is_subsequent_paragraph_of_list {
- text.push_str(" ");
- }
+ data
}
#[derive(Default)]
@@ -623,22 +445,7 @@ pub struct InfoPopover {
symbol_range: RangeInEditor,
pub blocks: Vec<HoverBlock>,
language: Option<Arc<Language>>,
- rendered_content: Option<RenderedInfo>,
-}
-
-#[derive(Debug, Clone)]
-struct RenderedInfo {
- theme_id: usize,
- text: String,
- highlights: Vec<(Range<usize>, HighlightStyle)>,
- region_ranges: Vec<Range<usize>>,
- regions: Vec<RenderedRegion>,
-}
-
-#[derive(Debug, Clone)]
-struct RenderedRegion {
- code: bool,
- link_url: Option<String>,
+ rendered_content: Option<RichText>,
}
impl InfoPopover {
@@ -647,63 +454,24 @@ 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;
- }
- }
-
let rendered_content = self.rendered_content.get_or_insert_with(|| {
render_blocks(
- style.theme_id,
&self.blocks,
self.project.read(cx).languages(),
self.language.as_ref(),
- style,
)
});
- MouseEventHandler::new::<InfoPopover, _>(0, cx, |_, cx| {
- let mut region_id = 0;
- let view_id = cx.view_id();
-
+ MouseEventHandler::new::<InfoPopover, _>(0, cx, move |_, cx| {
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, cx| {
- region_id += 1;
- let region = regions[ix].clone();
- if let Some(url) = region.link_url {
- cx.scene().push_cursor_region(CursorRegion {
- bounds,
- style: CursorStyle::PointingHand,
- });
- cx.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 {
- cx.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_content.element(
+ style.syntax.clone(),
+ style.text.clone(),
+ code_span_background_color,
+ cx,
+ ))
.contained()
.with_style(style.hover_popover.container)
})
@@ -799,11 +567,12 @@ mod tests {
InlayId,
};
use collections::BTreeSet;
- use gpui::fonts::Weight;
+ use gpui::fonts::{HighlightStyle, Underline, Weight};
use indoc::indoc;
use language::{language_settings::InlayHintSettings, Diagnostic, DiagnosticSet};
use lsp::LanguageServerId;
use project::{HoverBlock, HoverBlockKind};
+ use rich_text::Highlight;
use smol::stream::StreamExt;
use unindent::Unindent;
use util::test::marked_text_ranges;
@@ -1014,7 +783,7 @@ mod tests {
.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;
assert_eq!(
blocks,
@@ -1024,8 +793,7 @@ mod tests {
}],
);
- let style = editor.style(cx);
- let rendered = render_blocks(0, &blocks, &Default::default(), None, &style);
+ let rendered = render_blocks(&blocks, &Default::default(), None);
assert_eq!(
rendered.text,
code_str.trim(),
@@ -1217,7 +985,7 @@ mod tests {
expected_styles,
} in &rows[0..]
{
- let rendered = render_blocks(0, &blocks, &Default::default(), None, &style);
+ let rendered = render_blocks(&blocks, &Default::default(), None);
let (expected_text, ranges) = marked_text_ranges(expected_marked_text, false);
let expected_highlights = ranges
@@ -1228,8 +996,21 @@ mod tests {
rendered.text, expected_text,
"wrong text for input {blocks:?}"
);
+
+ let rendered_highlights: Vec<_> = rendered
+ .highlights
+ .iter()
+ .filter_map(|(range, highlight)| {
+ let style = match highlight {
+ Highlight::Id(id) => id.style(&style.syntax)?,
+ Highlight::Highlight(style) => style.clone(),
+ };
+ Some((range.clone(), style))
+ })
+ .collect();
+
assert_eq!(
- rendered.highlights, expected_highlights,
+ rendered_highlights, expected_highlights,
"wrong highlights for input {blocks:?}"
);
}
@@ -1,11 +1,11 @@
[package]
-name = "markdown_element"
+name = "rich_text"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
-path = "src/markdown_element.rs"
+path = "src/rich_text.rs"
doctest = false
[features]
@@ -6,94 +6,68 @@ use gpui::{
elements::Text,
fonts::{HighlightStyle, TextStyle, Underline, Weight},
platform::{CursorStyle, MouseButton},
- AnyElement, CursorRegion, Element, MouseRegion,
+ AnyElement, CursorRegion, Element, MouseRegion, ViewContext,
};
use language::{HighlightId, Language, LanguageRegistry};
use theme::SyntaxTheme;
#[derive(Debug, Clone, PartialEq, Eq)]
-enum Highlight {
+pub enum Highlight {
Id(HighlightId),
Highlight(HighlightStyle),
}
#[derive(Debug, Clone)]
-pub struct MarkdownData {
- text: String,
- highlights: Vec<(Range<usize>, Highlight)>,
- region_ranges: Vec<Range<usize>>,
- regions: Vec<RenderedRegion>,
+pub struct RichText {
+ pub text: String,
+ pub highlights: Vec<(Range<usize>, Highlight)>,
+ pub region_ranges: Vec<Range<usize>>,
+ pub regions: Vec<RenderedRegion>,
}
#[derive(Debug, Clone)]
-struct RenderedRegion {
+pub struct RenderedRegion {
code: bool,
link_url: Option<String>,
}
-pub struct MarkdownElement {
- data: Arc<MarkdownData>,
- syntax: Arc<SyntaxTheme>,
- style: TextStyle,
- code_span_background_color: Color,
-}
-
-impl MarkdownElement {
- pub fn new(
- data: Arc<MarkdownData>,
- style: TextStyle,
+impl RichText {
+ pub fn element<V: 'static>(
+ &self,
syntax: Arc<SyntaxTheme>,
+ style: TextStyle,
code_span_background_color: Color,
- ) -> Self {
- Self {
- data,
- style,
- syntax,
- code_span_background_color,
- }
- }
-}
-
-impl<V: 'static> Element<V> for MarkdownElement {
- type LayoutState = AnyElement<V>;
-
- type PaintState = ();
-
- fn layout(
- &mut self,
- constraint: gpui::SizeConstraint,
- view: &mut V,
- cx: &mut gpui::ViewContext<V>,
- ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
+ cx: &mut ViewContext<V>,
+ ) -> AnyElement<V> {
let mut region_id = 0;
let view_id = cx.view_id();
- let code_span_background_color = self.code_span_background_color;
- let data = self.data.clone();
- let mut element = Text::new(self.data.text.clone(), self.style.clone())
+ let regions = self.regions.clone();
+
+ enum Markdown {}
+ Text::new(self.text.clone(), style.clone())
.with_highlights(
- self.data
- .highlights
+ self.highlights
.iter()
.filter_map(|(range, highlight)| {
let style = match highlight {
- Highlight::Id(id) => id.style(&self.syntax)?,
+ Highlight::Id(id) => id.style(&syntax)?,
Highlight::Highlight(style) => style.clone(),
};
Some((range.clone(), style))
})
.collect::<Vec<_>>(),
)
- .with_custom_runs(self.data.region_ranges.clone(), move |ix, bounds, cx| {
+ .with_custom_runs(self.region_ranges.clone(), move |ix, bounds, cx| {
region_id += 1;
- let region = data.regions[ix].clone();
+ let region = regions[ix].clone();
if let Some(url) = region.link_url {
cx.scene().push_cursor_region(CursorRegion {
bounds,
style: CursorStyle::PointingHand,
});
cx.scene().push_mouse_region(
- MouseRegion::new::<Self>(view_id, region_id, bounds)
+ MouseRegion::new::<Markdown>(view_id, region_id, bounds)
.on_click::<V, _>(MouseButton::Left, move |_, _, cx| {
cx.platform().open_url(&url)
}),
@@ -109,55 +83,16 @@ impl<V: 'static> Element<V> for MarkdownElement {
}
})
.with_soft_wrap(true)
- .into_any();
-
- let constraint = element.layout(constraint, view, cx);
-
- (constraint, element)
- }
-
- fn paint(
- &mut self,
- bounds: gpui::geometry::rect::RectF,
- visible_bounds: gpui::geometry::rect::RectF,
- layout: &mut Self::LayoutState,
- view: &mut V,
- cx: &mut gpui::ViewContext<V>,
- ) -> Self::PaintState {
- layout.paint(bounds.origin(), visible_bounds, view, cx);
- }
-
- fn rect_for_text_range(
- &self,
- range_utf16: std::ops::Range<usize>,
- _: gpui::geometry::rect::RectF,
- _: gpui::geometry::rect::RectF,
- layout: &Self::LayoutState,
- _: &Self::PaintState,
- view: &V,
- cx: &gpui::ViewContext<V>,
- ) -> Option<gpui::geometry::rect::RectF> {
- layout.rect_for_text_range(range_utf16, view, cx)
- }
-
- fn debug(
- &self,
- _: gpui::geometry::rect::RectF,
- layout: &Self::LayoutState,
- _: &Self::PaintState,
- view: &V,
- cx: &gpui::ViewContext<V>,
- ) -> gpui::serde_json::Value {
- layout.debug(view, cx)
+ .into_any()
}
}
-pub fn render_markdown(block: String, language_registry: &Arc<LanguageRegistry>) -> MarkdownData {
- let mut text = String::new();
- let mut highlights = Vec::new();
- let mut region_ranges = Vec::new();
- let mut regions = Vec::new();
-
+pub fn render_markdown_mut(
+ block: &str,
+ language_registry: &Arc<LanguageRegistry>,
+ language: Option<&Arc<Language>>,
+ data: &mut RichText,
+) {
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
let mut bold_depth = 0;
@@ -167,13 +102,13 @@ pub fn render_markdown(block: String, language_registry: &Arc<LanguageRegistry>)
let mut list_stack = Vec::new();
for event in Parser::new_ext(&block, Options::all()) {
- let prev_len = text.len();
+ let prev_len = data.text.len();
match event {
Event::Text(t) => {
if let Some(language) = ¤t_language {
- render_code(&mut text, &mut highlights, t.as_ref(), language);
+ render_code(&mut data.text, &mut data.highlights, t.as_ref(), language);
} else {
- text.push_str(t.as_ref());
+ data.text.push_str(t.as_ref());
let mut style = HighlightStyle::default();
if bold_depth > 0 {
@@ -183,8 +118,8 @@ pub fn render_markdown(block: String, language_registry: &Arc<LanguageRegistry>)
style.italic = Some(true);
}
if let Some(link_url) = link_url.clone() {
- region_ranges.push(prev_len..text.len());
- regions.push(RenderedRegion {
+ data.region_ranges.push(prev_len..data.text.len());
+ data.regions.push(RenderedRegion {
link_url: Some(link_url),
code: false,
});
@@ -196,26 +131,27 @@ pub fn render_markdown(block: String, language_registry: &Arc<LanguageRegistry>)
if style != HighlightStyle::default() {
let mut new_highlight = true;
- if let Some((last_range, last_style)) = highlights.last_mut() {
+ if let Some((last_range, last_style)) = data.highlights.last_mut() {
if last_range.end == prev_len
&& last_style == &Highlight::Highlight(style)
{
- last_range.end = text.len();
+ last_range.end = data.text.len();
new_highlight = false;
}
}
if new_highlight {
- highlights.push((prev_len..text.len(), Highlight::Highlight(style)));
+ data.highlights
+ .push((prev_len..data.text.len(), Highlight::Highlight(style)));
}
}
}
}
Event::Code(t) => {
- text.push_str(t.as_ref());
- region_ranges.push(prev_len..text.len());
+ data.text.push_str(t.as_ref());
+ data.region_ranges.push(prev_len..data.text.len());
if link_url.is_some() {
- highlights.push((
- prev_len..text.len(),
+ data.highlights.push((
+ prev_len..data.text.len(),
Highlight::Highlight(HighlightStyle {
underline: Some(Underline {
thickness: 1.0.into(),
@@ -225,26 +161,26 @@ pub fn render_markdown(block: String, language_registry: &Arc<LanguageRegistry>)
}),
));
}
- regions.push(RenderedRegion {
+ data.regions.push(RenderedRegion {
code: true,
link_url: link_url.clone(),
});
}
Event::Start(tag) => match tag {
- Tag::Paragraph => new_paragraph(&mut text, &mut list_stack),
+ Tag::Paragraph => new_paragraph(&mut data.text, &mut list_stack),
Tag::Heading(_, _, _) => {
- new_paragraph(&mut text, &mut list_stack);
+ new_paragraph(&mut data.text, &mut list_stack);
bold_depth += 1;
}
Tag::CodeBlock(kind) => {
- new_paragraph(&mut text, &mut list_stack);
+ new_paragraph(&mut data.text, &mut list_stack);
current_language = if let CodeBlockKind::Fenced(language) = kind {
language_registry
.language_for_name(language.as_ref())
.now_or_never()
.and_then(Result::ok)
} else {
- None
+ language.cloned()
}
}
Tag::Emphasis => italic_depth += 1,
@@ -257,18 +193,18 @@ pub fn render_markdown(block: String, language_registry: &Arc<LanguageRegistry>)
let len = list_stack.len();
if let Some((list_number, has_content)) = list_stack.last_mut() {
*has_content = false;
- if !text.is_empty() && !text.ends_with('\n') {
- text.push('\n');
+ if !data.text.is_empty() && !data.text.ends_with('\n') {
+ data.text.push('\n');
}
for _ in 0..len - 1 {
- text.push_str(" ");
+ data.text.push_str(" ");
}
if let Some(number) = list_number {
- text.push_str(&format!("{}. ", number));
+ data.text.push_str(&format!("{}. ", number));
*number += 1;
*has_content = false;
} else {
- text.push_str("- ");
+ data.text.push_str("- ");
}
}
}
@@ -283,21 +219,33 @@ pub fn render_markdown(block: String, language_registry: &Arc<LanguageRegistry>)
Tag::List(_) => drop(list_stack.pop()),
_ => {}
},
- Event::HardBreak => text.push('\n'),
- Event::SoftBreak => text.push(' '),
+ Event::HardBreak => data.text.push('\n'),
+ Event::SoftBreak => data.text.push(' '),
_ => {}
}
}
+}
- MarkdownData {
- text: text.trim().to_string(),
- highlights,
- region_ranges,
- regions,
- }
+pub fn render_markdown(
+ block: String,
+ language_registry: &Arc<LanguageRegistry>,
+ language: Option<&Arc<Language>>,
+) -> RichText {
+ let mut data = RichText {
+ text: Default::default(),
+ highlights: Default::default(),
+ region_ranges: Default::default(),
+ regions: Default::default(),
+ };
+
+ render_markdown_mut(&block, language_registry, language, &mut data);
+
+ data.text = data.text.trim().to_string();
+
+ data
}
-fn render_code(
+pub fn render_code(
text: &mut String,
highlights: &mut Vec<(Range<usize>, Highlight)>,
content: &str,
@@ -313,7 +261,7 @@ fn render_code(
}
}
-fn new_paragraph(text: &mut String, list_stack: &mut Vec<(Option<u64>, bool)>) {
+pub fn new_paragraph(text: &mut String, list_stack: &mut Vec<(Option<u64>, bool)>) {
let mut is_subsequent_paragraph_of_list = false;
if let Some((_, has_content)) = list_stack.last_mut() {
if *has_content {