diff --git a/src/ast/drawer.rs b/src/ast/drawer.rs index e6225b0..f9262c8 100644 --- a/src/ast/drawer.rs +++ b/src/ast/drawer.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use super::{filter_token, SyntaxKind, Token}; +use super::{filter_token, Drawer, SyntaxKind, Token}; use crate::ast::PropertyDrawer; impl PropertyDrawer { @@ -62,3 +62,24 @@ impl PropertyDrawer { self.iter().collect() } } + +impl Drawer { + /// ```rust + /// use orgize::{Org, ast::Drawer}; + /// + /// let org = Org::parse("* Heading\n:LOGBOOK:\n:END:"); + /// let drawer = org.first_node::().unwrap(); + /// assert_eq!(drawer.name(), "LOGBOOK"); + /// ``` + pub fn name(&self) -> Token { + self.syntax + .first_child() + .and_then(|n| { + n.children_with_tokens() + .filter_map(|e| e.into_token()) + .find(|e| e.kind() == SyntaxKind::TEXT) + }) + .map(|t| Token(Some(t))) + .unwrap_or_default() + } +} diff --git a/src/ast/headline.rs b/src/ast/headline.rs index 5ae33b1..39c99dd 100644 --- a/src/ast/headline.rs +++ b/src/ast/headline.rs @@ -1,8 +1,8 @@ -use rowan::NodeOrToken; +use rowan::{ast::AstNode, NodeOrToken}; use crate::{syntax::SyntaxKind, SyntaxElement}; -use super::{filter_token, Headline, Timestamp, Token}; +use super::{filter_token, Clock, Drawer, Headline, Section, Timestamp, Token}; #[derive(Debug, Copy, Clone, PartialEq)] pub enum TodoType { @@ -216,6 +216,39 @@ impl Headline { .find_map(filter_token(SyntaxKind::TEXT)) }) } + + /// Returns an iterator of clock element affiliated with this headline + /// + /// ```rust + /// use orgize::{Org, ast::Headline}; + /// + /// let org = Org::parse(r#"* TODO + /// foo + /// :LOGBOOK: + /// bar + /// CLOCK: + /// CLOCK: [2024-10-12] + /// baz + /// CLOCK: [2024-10-12] + /// [2024-10-12] + /// :END: + /// foo"#); + /// let hdl = org.first_node::().unwrap(); + /// assert_eq!(hdl.clocks().count(), 2); + /// ``` + pub fn clocks(&self) -> impl Iterator { + self.syntax + .children() + .flat_map(Section::cast) + .flat_map(|x| x.syntax.children().filter_map(Drawer::cast)) + .filter(|d| d.name().eq_ignore_ascii_case("LOGBOOK")) + .filter_map(|d| { + d.syntax + .children() + .find(|children| children.kind() == SyntaxKind::DRAWER_CONTENT) + }) + .flat_map(|x| x.children().filter_map(Clock::cast)) + } } // pub enum DocumentOrHeadline { diff --git a/src/syntax/drawer.rs b/src/syntax/drawer.rs index 4bbf752..471571c 100644 --- a/src/syntax/drawer.rs +++ b/src/syntax/drawer.rs @@ -11,6 +11,7 @@ use super::{ blank_lines, colon_token, eol_or_eof, line_starts_iter, node, plus_token, trim_line_end, GreenElement, NodeBuilder, }, + element::element_nodes, input::Input, SyntaxKind::*, }; @@ -68,7 +69,11 @@ fn drawer_node_base(input: Input) -> IResult { let (input, post_blank) = blank_lines(input)?; let mut children = vec![begin]; children.extend(pre_blank); - children.push(contents.text_token()); + if !contents.is_empty() { + children.push(node(DRAWER_CONTENT, element_nodes(contents)?)); + } else { + children.push(node(DRAWER_CONTENT, [])); + } children.push(end); children.extend(post_blank); @@ -161,7 +166,13 @@ fn parse() { TEXT@1..7 "DRAWER" COLON@7..8 ":" NEW_LINE@8..9 "\n" - TEXT@9..26 " :CUSTOM_ID: id\n" + DRAWER_CONTENT@9..26 + PARAGRAPH@9..26 + TEXT@9..18 " :CUSTOM" + SUBSCRIPT@18..21 + UNDERSCORE@18..19 "_" + TEXT@19..21 "ID" + TEXT@21..26 ": id\n" DRAWER_END@26..33 WHITESPACE@26..28 " " COLON@28..29 ":" @@ -186,7 +197,7 @@ fn parse() { COLON@7..8 ":" NEW_LINE@8..9 "\n" BLANK_LINE@9..10 "\n" - TEXT@10..10 "" + DRAWER_CONTENT@10..10 DRAWER_END@10..18 WHITESPACE@10..12 " " COLON@12..13 ":" diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index a2659b6..64a008c 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -152,6 +152,7 @@ pub enum SyntaxKind { DRAWER, DRAWER_BEGIN, DRAWER_END, + DRAWER_CONTENT, KEYWORD, BABEL_CALL, AFFILIATED_KEYWORD,