markdown preview: Break up list items into individual blocks (#10852)
Fixes a panic related to rendering checkboxes, see #10824. Currently we are rendering a list into a single block, meaning the whole block has to be rendered when it is visible on screen. This would lead to performance problems when a single list block contained a lot of items (especially if it contained checkboxes). This PR splits up list items into separate blocks, meaning only the actual visible list items on screen get rendered, instead of the whole list. A nice side effect of the refactoring is, that you can actually click on individual list items now: https://github.com/zed-industries/zed/assets/53836821/5ef4200c-bd85-4e96-a8bf-e0c8b452f762 Release Notes: - Improved rendering performance of list elements inside the markdown preview --------- Co-authored-by: Remco <djsmits12@gmail.com>
This commit is contained in:
parent
664f779eb4
commit
9329ef1d78
6 changed files with 286 additions and 279 deletions
|
@ -1,7 +1,8 @@
|
|||
use crate::markdown_elements::{
|
||||
HeadingLevel, Link, ParsedMarkdown, ParsedMarkdownBlockQuote, ParsedMarkdownCodeBlock,
|
||||
ParsedMarkdownElement, ParsedMarkdownHeading, ParsedMarkdownList, ParsedMarkdownListItemType,
|
||||
ParsedMarkdownTable, ParsedMarkdownTableAlignment, ParsedMarkdownTableRow, ParsedMarkdownText,
|
||||
ParsedMarkdownElement, ParsedMarkdownHeading, ParsedMarkdownListItem,
|
||||
ParsedMarkdownListItemType, ParsedMarkdownTable, ParsedMarkdownTableAlignment,
|
||||
ParsedMarkdownTableRow, ParsedMarkdownText,
|
||||
};
|
||||
use gpui::{
|
||||
div, px, rems, AbsoluteLength, AnyElement, DefiniteLength, Div, Element, ElementId,
|
||||
|
@ -110,7 +111,7 @@ pub fn render_markdown_block(block: &ParsedMarkdownElement, cx: &mut RenderConte
|
|||
match block {
|
||||
Paragraph(text) => render_markdown_paragraph(text, cx),
|
||||
Heading(heading) => render_markdown_heading(heading, cx),
|
||||
List(list) => render_markdown_list(list, cx),
|
||||
ListItem(list_item) => render_markdown_list_item(list_item, cx),
|
||||
Table(table) => render_markdown_table(table, cx),
|
||||
BlockQuote(block_quote) => render_markdown_block_quote(block_quote, cx),
|
||||
CodeBlock(code_block) => render_markdown_code_block(code_block, cx),
|
||||
|
@ -146,79 +147,77 @@ fn render_markdown_heading(parsed: &ParsedMarkdownHeading, cx: &mut RenderContex
|
|||
.into_any()
|
||||
}
|
||||
|
||||
fn render_markdown_list(parsed: &ParsedMarkdownList, cx: &mut RenderContext) -> AnyElement {
|
||||
fn render_markdown_list_item(
|
||||
parsed: &ParsedMarkdownListItem,
|
||||
cx: &mut RenderContext,
|
||||
) -> AnyElement {
|
||||
use ParsedMarkdownListItemType::*;
|
||||
|
||||
let mut items = vec![];
|
||||
for item in &parsed.children {
|
||||
let padding = rems((item.depth - 1) as f32 * 0.25);
|
||||
let padding = rems((parsed.depth - 1) as f32);
|
||||
|
||||
let bullet = match &item.item_type {
|
||||
Ordered(order) => format!("{}.", order).into_any_element(),
|
||||
Unordered => "•".into_any_element(),
|
||||
Task(checked, range) => div()
|
||||
.id(cx.next_id(range))
|
||||
.mt(px(3.))
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"checkbox",
|
||||
if *checked {
|
||||
Selection::Selected
|
||||
} else {
|
||||
Selection::Unselected
|
||||
},
|
||||
)
|
||||
.when_some(
|
||||
cx.checkbox_clicked_callback.clone(),
|
||||
|this, callback| {
|
||||
this.on_click({
|
||||
let range = range.clone();
|
||||
move |selection, cx| {
|
||||
let checked = match selection {
|
||||
Selection::Selected => true,
|
||||
Selection::Unselected => false,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if cx.modifiers().secondary() {
|
||||
callback(checked, range.clone(), cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
let bullet = match &parsed.item_type {
|
||||
Ordered(order) => format!("{}.", order).into_any_element(),
|
||||
Unordered => "•".into_any_element(),
|
||||
Task(checked, range) => div()
|
||||
.id(cx.next_id(range))
|
||||
.mt(px(3.))
|
||||
.child(
|
||||
Checkbox::new(
|
||||
"checkbox",
|
||||
if *checked {
|
||||
Selection::Selected
|
||||
} else {
|
||||
Selection::Unselected
|
||||
},
|
||||
)
|
||||
.hover(|s| s.cursor_pointer())
|
||||
.tooltip(|cx| {
|
||||
let secondary_modifier = Keystroke {
|
||||
key: "".to_string(),
|
||||
modifiers: Modifiers::secondary_key(),
|
||||
ime_key: None,
|
||||
};
|
||||
Tooltip::text(
|
||||
format!("{}-click to toggle the checkbox", secondary_modifier),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.into_any_element(),
|
||||
};
|
||||
let bullet = div().mr_2().child(bullet);
|
||||
.when_some(
|
||||
cx.checkbox_clicked_callback.clone(),
|
||||
|this, callback| {
|
||||
this.on_click({
|
||||
let range = range.clone();
|
||||
move |selection, cx| {
|
||||
let checked = match selection {
|
||||
Selection::Selected => true,
|
||||
Selection::Unselected => false,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let contents: Vec<AnyElement> = item
|
||||
.contents
|
||||
.iter()
|
||||
.map(|c| render_markdown_block(c.as_ref(), cx))
|
||||
.collect();
|
||||
if cx.modifiers().secondary() {
|
||||
callback(checked, range.clone(), cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
),
|
||||
)
|
||||
.hover(|s| s.cursor_pointer())
|
||||
.tooltip(|cx| {
|
||||
let secondary_modifier = Keystroke {
|
||||
key: "".to_string(),
|
||||
modifiers: Modifiers::secondary_key(),
|
||||
ime_key: None,
|
||||
};
|
||||
Tooltip::text(
|
||||
format!("{}-click to toggle the checkbox", secondary_modifier),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.into_any_element(),
|
||||
};
|
||||
let bullet = div().mr_2().child(bullet);
|
||||
|
||||
let item = h_flex()
|
||||
.pl(DefiniteLength::Absolute(AbsoluteLength::Rems(padding)))
|
||||
.items_start()
|
||||
.children(vec![bullet, div().children(contents).pr_4().w_full()]);
|
||||
let contents: Vec<AnyElement> = parsed
|
||||
.content
|
||||
.iter()
|
||||
.map(|c| render_markdown_block(c, cx))
|
||||
.collect();
|
||||
|
||||
items.push(item);
|
||||
}
|
||||
let item = h_flex()
|
||||
.pl(DefiniteLength::Absolute(AbsoluteLength::Rems(padding)))
|
||||
.items_start()
|
||||
.children(vec![bullet, div().children(contents).pr_4().w_full()]);
|
||||
|
||||
cx.with_common_p(div()).children(items).into_any()
|
||||
cx.with_common_p(item).into_any()
|
||||
}
|
||||
|
||||
fn render_markdown_table(parsed: &ParsedMarkdownTable, cx: &mut RenderContext) -> AnyElement {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue