pub const ELEMENT_QUERY: &str = r"
(atx_heading heading_content: (_) @func_name) @function
(setext_heading heading_content: (_) @func_name) @function
";
pub const CALL_QUERY: &str = "";
#[cfg(all(test, feature = "lang-markdown"))]
mod tests {
use tree_sitter::{Parser, StreamingIterator};
fn parse_and_query(src: &str, query_str: &str) -> Vec<String> {
let language = tree_sitter_md::LANGUAGE;
let mut parser = Parser::new();
parser
.set_language(&language.into())
.expect("failed to set language");
let tree = parser.parse(src, None).expect("parse failed");
let query = tree_sitter::Query::new(&language.into(), query_str).expect("invalid query");
let mut cursor = tree_sitter::QueryCursor::new();
let mut matches = cursor.matches(&query, tree.root_node(), src.as_bytes());
let func_name_idx = query
.capture_index_for_name("func_name")
.expect("no func_name capture");
let mut names = Vec::new();
while let Some(m) = matches.next() {
for cap in m.captures {
if cap.index == func_name_idx {
let text = &src[cap.node.start_byte()..cap.node.end_byte()];
names.push(text.trim().to_owned());
}
}
}
names
}
#[test]
fn test_atx_headings_extracted() {
let src = "# Introduction\n\n## Installation\n\n### Details\n";
let names = parse_and_query(src, super::ELEMENT_QUERY);
assert_eq!(names, vec!["Introduction", "Installation", "Details"]);
}
#[test]
fn test_setext_heading_extracted() {
let src = "Overview\n========\n\nSetup\n-----\n";
let names = parse_and_query(src, super::ELEMENT_QUERY);
assert_eq!(names, vec!["Overview", "Setup"]);
}
#[test]
fn test_no_headings_returns_empty() {
let src = "Just some prose.\n\nNo headings here.\n";
let names = parse_and_query(src, super::ELEMENT_QUERY);
assert!(names.is_empty());
}
#[test]
fn test_code_fence_not_extracted() {
let src = "```python\n# not a heading\nprint('hello')\n```\n";
let names = parse_and_query(src, super::ELEMENT_QUERY);
assert!(names.is_empty());
}
}