feat: impl md ordered list

This commit is contained in:
2026-05-13 00:52:13 +08:00
parent ee226178b2
commit 53993fed93
2 changed files with 57 additions and 39 deletions

View File

@@ -1,9 +1,8 @@
// markdown treesitter playground: https://ikatyang.github.io/tree-sitter-markdown/
use std::ops::Range;
use std::{ops::Range, sync::LazyLock};
use gpui::{AppContext, ParentElement, Refineable, RenderOnce, Styled, div, px, relative, rems};
use tree_sitter::Node;
use gpui::{AppContext, ParentElement, Refineable, Styled, div, px, relative, rems};
use crate::{app, theme};
@@ -93,7 +92,7 @@ pub(crate) struct MarkdownText {
enum ContentBlock {
Text {
decoration: Option<&'static str>,
decoration: Option<gpui::SharedString>,
text: gpui::SharedString,
highlights: Vec<(Range<usize>, gpui::HighlightStyle)>,
links: Vec<(Range<usize>, gpui::SharedString)>,
@@ -147,6 +146,9 @@ impl MarkdownText {
fn on_open_link(&self, _link: &str, _cx: &gpui::Context<Self>) {}
fn render_tree(&mut self, tree: &tree_sitter::Tree, theme: &theme::Theme) {
static ORDERED_LIST_MARKER_REGEX: LazyLock<regex::Regex> =
LazyLock::new(|| regex::Regex::new(r"^\d+\.$").unwrap());
let mut cursor = tree.walk();
cursor.goto_first_child();
@@ -280,7 +282,7 @@ impl MarkdownText {
return false;
}
let mut list_index = 0;
let mut list_index: Option<usize> = None;
loop {
if cursor.node().kind_id() != MARKDOWN_KIND_ID_LIST_ITEM_TIGHT
@@ -298,46 +300,61 @@ impl MarkdownText {
let marker_node = cursor.node();
let marker_content = &content[marker_node.byte_range()];
match marker_content {
let list_marker_char = match marker_content {
// unordered list item
| "-" | "+" | "*" => {
// go to paragraph sibling node
let block = if cursor.goto_next_sibling() {
let mut b = block_for_node(cursor, content, 0, theme);
match b {
| ContentBlock::Text {
ref mut decoration, ..
} => *decoration = Some(""),
}
b
} else {
ContentBlock::Text {
decoration: Some(""),
text: gpui::SharedString::default(),
highlights: Vec::new(),
links: Vec::new(),
style: gpui::StyleRefinement::default(),
}
}
.text_sm()
.text_color(theme.colors.text)
.p(rems(indentation as f32));
| "-" | "+" | "*" => Some("".to_string()),
blocks.push(block);
// if there is a nested tight_light after paragraph
// render it recursively
if cursor.goto_next_sibling()
&& cursor.node().kind_id() == MARKDOWN_KIND_ID_TIGHT_LIST
{
render_list_node(cursor, content, blocks, theme, indentation + 1);
}
| marker_content if ORDERED_LIST_MARKER_REGEX.is_match(marker_content) => {
let i = list_index.get_or_insert_with(|| {
marker_content
.strip_suffix('.')
.unwrap()
.parse::<usize>()
.unwrap()
});
let j = *i;
*i = j + 1;
Some(format!("{j}."))
}
| _ => {
| _ => None,
};
let Some(list_marker_char) = list_marker_char else {
render_fallback_content(&cursor, content, blocks);
return false;
};
// go to paragraph sibling node
let block = if cursor.goto_next_sibling() {
let mut b = block_for_node(cursor, content, 0, theme);
match b {
| ContentBlock::Text {
ref mut decoration, ..
} => *decoration = Some(list_marker_char.into()),
}
b
} else {
ContentBlock::Text {
decoration: Some(list_marker_char.into()),
text: gpui::SharedString::default(),
highlights: Vec::new(),
links: Vec::new(),
style: gpui::StyleRefinement::default(),
}
}
.text_sm()
.text_color(theme.colors.text)
.p(rems(indentation as f32));
blocks.push(block);
// if there is a nested tight_light after paragraph
// render it recursively
if cursor.goto_next_sibling()
&& cursor.node().kind_id() == MARKDOWN_KIND_ID_TIGHT_LIST
{
render_list_node(cursor, content, blocks, theme, indentation + 1);
}
// go back to list_item node
@@ -549,7 +566,7 @@ impl gpui::Render for MarkdownText {
.flex_row()
.gap_2()
.items_start()
.child(*d)
.child(d.clone())
.child(div().flex_1().min_w_0().child(content)),
| None => div().w_full().child(content),
};