Files
herolib/lib/data/markdownparser2/navigator.v
2025-03-07 21:03:55 +01:00

272 lines
6.4 KiB
V

module markdownparser2
// Navigator provides an easy way to navigate through the document structure
@[heap]
pub struct Navigator {
pub:
doc MarkdownDocument
pub mut:
current_element &MarkdownElement
}
// Creates a new navigator for a markdown document
pub fn new_navigator(doc MarkdownDocument) Navigator {
return Navigator{
doc: doc
current_element: doc.root
}
}
// Reset the navigator to the root element
pub fn (mut n Navigator) reset() {
n.current_element = n.doc.root
}
// Find an element by type
pub fn (mut n Navigator) find_by_type(typ ElementType) ?&MarkdownElement {
return n.find_by_type_from(n.doc.root, typ)
}
// Find an element by type starting from a specific element
fn (mut n Navigator) find_by_type_from(element &MarkdownElement, typ ElementType) ?&MarkdownElement {
if element.typ == typ {
n.current_element = element
return element
}
for child in element.children {
if child.typ == typ {
n.current_element = child
return child
}
if result := n.find_by_type_from(child, typ) {
return result
}
}
return none
}
// Find all elements by type
pub fn (mut n Navigator) find_all_by_type(typ ElementType) []&MarkdownElement {
return n.find_all_by_type_from(n.doc.root, typ)
}
// Find all elements by type starting from a specific element
fn (mut n Navigator) find_all_by_type_from(element &MarkdownElement, typ ElementType) []&MarkdownElement {
mut results := []&MarkdownElement{}
if element.typ == typ {
results << element
}
for child in element.children {
if child.typ == typ {
results << child
}
results << n.find_all_by_type_from(child, typ)
}
return results
}
// Find an element by content
pub fn (mut n Navigator) find_by_content(text string) ?&MarkdownElement {
return n.find_by_content_from(n.doc.root, text)
}
// Find an element by content starting from a specific element
fn (mut n Navigator) find_by_content_from(element &MarkdownElement, text string) ?&MarkdownElement {
if element.content.contains(text) {
n.current_element = element
return element
}
for child in element.children {
if child.content.contains(text) {
n.current_element = child
return child
}
if result := n.find_by_content_from(child, text) {
return result
}
}
return none
}
// Find all elements by content
pub fn (mut n Navigator) find_all_by_content(text string) []&MarkdownElement {
return n.find_all_by_content_from(n.doc.root, text)
}
// Find all elements by content starting from a specific element
fn (mut n Navigator) find_all_by_content_from(element &MarkdownElement, text string) []&MarkdownElement {
mut results := []&MarkdownElement{}
if element.content.contains(text) {
results << element
}
for child in element.children {
if child.content.contains(text) {
results << child
}
results << n.find_all_by_content_from(child, text)
}
return results
}
// Find an element by attribute
pub fn (mut n Navigator) find_by_attribute(key string, value string) ?&MarkdownElement {
return n.find_by_attribute_from(n.doc.root, key, value)
}
// Find an element by attribute starting from a specific element
fn (mut n Navigator) find_by_attribute_from(element &MarkdownElement, key string, value string) ?&MarkdownElement {
if element.attributes[key] == value {
n.current_element = element
return element
}
for child in element.children {
if child.attributes[key] == value {
n.current_element = child
return child
}
if result := n.find_by_attribute_from(child, key, value) {
return result
}
}
return none
}
// Find all elements by attribute
pub fn (mut n Navigator) find_all_by_attribute(key string, value string) []&MarkdownElement {
return n.find_all_by_attribute_from(n.doc.root, key, value)
}
// Find all elements by attribute starting from a specific element
fn (mut n Navigator) find_all_by_attribute_from(element &MarkdownElement, key string, value string) []&MarkdownElement {
mut results := []&MarkdownElement{}
if element.attributes[key] == value {
results << element
}
for child in element.children {
if child.attributes[key] == value {
results << child
}
results << n.find_all_by_attribute_from(child, key, value)
}
return results
}
// Find the parent of an element
pub fn (mut n Navigator) find_parent(target &MarkdownElement) ?&MarkdownElement {
return n.find_parent_from(n.doc.root, target)
}
// Find the parent of an element starting from a specific element
fn (mut n Navigator) find_parent_from(root &MarkdownElement, target &MarkdownElement) ?&MarkdownElement {
for child in root.children {
if child == target {
n.current_element = root
return root
}
if result := n.find_parent_from(child, target) {
return result
}
}
return none
}
// Get the parent of the current element
pub fn (mut n Navigator) parent() ?&MarkdownElement {
return n.find_parent(n.current_element)
}
// Get the next sibling of the current element
pub fn (mut n Navigator) next_sibling() ?&MarkdownElement {
parent := n.parent() or { return none }
mut found := false
for child in parent.children {
if found {
n.current_element = child
return child
}
if child == n.current_element {
found = true
}
}
return none
}
// Get the previous sibling of the current element
pub fn (mut n Navigator) prev_sibling() ?&MarkdownElement {
parent := n.parent() or { return none }
mut prev := &MarkdownElement(unsafe { nil })
for i, child in parent.children {
if child == n.current_element && prev != unsafe { nil } {
n.current_element = prev
return prev
}
if i < parent.children.len - 1 {
prev = parent.children[i]
}
}
return none
}
// Get the first child of the current element
pub fn (mut n Navigator) first_child() ?&MarkdownElement {
if n.current_element.children.len == 0 {
return none
}
n.current_element = n.current_element.children[0]
return n.current_element
}
// Get the last child of the current element
pub fn (mut n Navigator) last_child() ?&MarkdownElement {
if n.current_element.children.len == 0 {
return none
}
n.current_element = n.current_element.children[n.current_element.children.len - 1]
return n.current_element
}
// Get all footnotes in the document
pub fn (n Navigator) footnotes() map[string]&MarkdownElement {
return n.doc.footnotes
}
// Get a footnote by identifier
pub fn (n Navigator) footnote(id string) ?&MarkdownElement {
if id in n.doc.footnotes {
return unsafe { n.doc.footnotes[id] }
}
return none
}