use crate::widgets::markdown_widget::state::{CollapseState, ScrollState};
use crate::widgets::markdown_widget::foundation::elements::{render, ElementKind, MarkdownElement};
use crate::widgets::markdown_widget::foundation::events::MarkdownDoubleClickEvent;
use crate::widgets::markdown_widget::foundation::parser::render_markdown_to_elements;
use super::element_to_plain_text::element_to_plain_text;
fn element_kind_to_string(kind: &ElementKind) -> String {
match kind {
ElementKind::Heading { level, .. } => format!("Heading (H{})", level),
ElementKind::HeadingBorder { .. } => "Heading Border".to_string(),
ElementKind::CodeBlockHeader { language, .. } => {
format!(
"Code Block Header ({})",
if language.is_empty() {
"text"
} else {
language
}
)
}
ElementKind::CodeBlockContent { .. } => "Code Block Content".to_string(),
ElementKind::CodeBlockBorder { .. } => "Code Block Border".to_string(),
ElementKind::Paragraph(_) => "Paragraph".to_string(),
ElementKind::ListItem { ordered, depth, .. } => {
if *ordered {
format!("Ordered List Item (depth {})", depth)
} else {
format!("Unordered List Item (depth {})", depth)
}
}
ElementKind::Blockquote { depth, .. } => format!("Blockquote (depth {})", depth),
ElementKind::TableRow { is_header, .. } => {
if *is_header {
"Table Header".to_string()
} else {
"Table Row".to_string()
}
}
ElementKind::TableBorder(_) => "Table Border".to_string(),
ElementKind::HorizontalRule => "Horizontal Rule".to_string(),
ElementKind::Empty => "Empty".to_string(),
ElementKind::Frontmatter { .. } => "Frontmatter".to_string(),
ElementKind::FrontmatterStart { .. } => "Frontmatter Start".to_string(),
ElementKind::FrontmatterField { key, .. } => format!("Frontmatter Field ({})", key),
ElementKind::FrontmatterEnd => "Frontmatter End".to_string(),
ElementKind::Expandable { .. } => "Expandable Content".to_string(),
ElementKind::ExpandToggle { .. } => "Expand Toggle".to_string(),
}
}
fn should_render_line(element: &MarkdownElement, _idx: usize, collapse: &CollapseState) -> bool {
if let ElementKind::Heading { section_id, .. } = &element.kind {
if let Some((_, Some(parent))) = collapse.get_hierarchy(*section_id) {
if collapse.is_section_collapsed(parent) {
return false;
}
}
return true;
}
if matches!(element.kind, ElementKind::Frontmatter { .. }) {
return true;
}
if matches!(element.kind, ElementKind::FrontmatterStart { .. }) {
return true;
}
if matches!(
element.kind,
ElementKind::FrontmatterField { .. } | ElementKind::FrontmatterEnd
) {
if collapse.is_section_collapsed(0) {
return false;
}
return true;
}
if let Some(section_id) = element.section_id {
if collapse.is_section_collapsed(section_id) {
return false;
}
}
true
}
pub fn get_line_at_position(
y: usize,
width: usize,
content: &str,
scroll: &ScrollState,
collapse: &CollapseState,
) -> Option<MarkdownDoubleClickEvent> {
let elements = render_markdown_to_elements(content, true);
let document_y = y + scroll.scroll_offset;
let mut visual_line_idx = 0;
let mut logical_line_num = 0;
for (idx, element) in elements.iter().enumerate() {
if !should_render_line(element, idx, collapse) {
continue;
}
logical_line_num += 1;
let rendered = render(element, width);
let line_count = rendered.len();
if document_y >= visual_line_idx && document_y < visual_line_idx + line_count {
let line_kind = element_kind_to_string(&element.kind);
let text_content = element_to_plain_text(&element.kind);
return Some(MarkdownDoubleClickEvent {
line_number: logical_line_num,
line_kind,
content: text_content,
});
}
visual_line_idx += line_count;
}
None
}