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:
Bennet Bo Fenner 2024-04-26 21:34:45 +02:00 committed by GitHub
parent 664f779eb4
commit 9329ef1d78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 286 additions and 279 deletions

View file

@ -8,8 +8,7 @@ use std::{fmt::Display, ops::Range, path::PathBuf};
#[cfg_attr(test, derive(PartialEq))]
pub enum ParsedMarkdownElement {
Heading(ParsedMarkdownHeading),
/// An ordered or unordered list of items.
List(ParsedMarkdownList),
ListItem(ParsedMarkdownListItem),
Table(ParsedMarkdownTable),
BlockQuote(ParsedMarkdownBlockQuote),
CodeBlock(ParsedMarkdownCodeBlock),
@ -22,7 +21,7 @@ impl ParsedMarkdownElement {
pub fn source_range(&self) -> Range<usize> {
match self {
Self::Heading(heading) => heading.source_range.clone(),
Self::List(list) => list.source_range.clone(),
Self::ListItem(list_item) => list_item.source_range.clone(),
Self::Table(table) => table.source_range.clone(),
Self::BlockQuote(block_quote) => block_quote.source_range.clone(),
Self::CodeBlock(code_block) => code_block.source_range.clone(),
@ -30,6 +29,10 @@ impl ParsedMarkdownElement {
Self::HorizontalRule(range) => range.clone(),
}
}
pub fn is_list_item(&self) -> bool {
matches!(self, Self::ListItem(_))
}
}
#[derive(Debug)]
@ -38,20 +41,14 @@ pub struct ParsedMarkdown {
pub children: Vec<ParsedMarkdownElement>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct ParsedMarkdownList {
pub source_range: Range<usize>,
pub children: Vec<ParsedMarkdownListItem>,
}
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct ParsedMarkdownListItem {
pub source_range: Range<usize>,
/// How many indentations deep this item is.
pub depth: u16,
pub item_type: ParsedMarkdownListItemType,
pub contents: Vec<Box<ParsedMarkdownElement>>,
pub content: Vec<ParsedMarkdownElement>,
}
#[derive(Debug)]
@ -129,7 +126,7 @@ impl ParsedMarkdownTableRow {
#[cfg_attr(test, derive(PartialEq))]
pub struct ParsedMarkdownBlockQuote {
pub source_range: Range<usize>,
pub children: Vec<Box<ParsedMarkdownElement>>,
pub children: Vec<ParsedMarkdownElement>,
}
#[derive(Debug)]