From 9b8aec02a4aed18dd06a757e5bc9563c238193af Mon Sep 17 00:00:00 2001 From: PoiScript Date: Thu, 9 May 2024 16:41:43 +0800 Subject: [PATCH] feat: support top-level properties drawer (#78) --- src/ast/document.rs | 22 ++++++++++++++-------- src/ast/generate.js | 1 + src/ast/generated.rs | 3 +++ src/syntax/document.rs | 10 ++++++++++ src/syntax/drawer.rs | 4 +++- 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/ast/document.rs b/src/ast/document.rs index fe7545e..ab190ee 100644 --- a/src/ast/document.rs +++ b/src/ast/document.rs @@ -2,29 +2,35 @@ use rowan::ast::AstNode; use crate::Org; -use super::{Document, Keyword, SyntaxKind}; +use super::{Document, Keyword}; impl Document { + /// Returns an iterator of keywords in zeroth section + /// /// ```rust /// use orgize::{Org, ast::Document}; /// /// let org = Org::parse(r#" /// #+TITLE: hello /// #+TITLE: world - /// #+DATE: tody - /// #+AUTHOR: poi"#); + /// #+DATE: today + /// #+AUTHOR: poi + /// * headline + /// #+SOMETHING:"#); /// let doc = org.first_node::().unwrap(); /// assert_eq!(doc.keywords().count(), 4); /// ``` pub fn keywords(&self) -> impl Iterator { - self.syntax - .first_child() - .filter(|c| c.kind() == SyntaxKind::SECTION) + self.section() .into_iter() - .flat_map(|section| section.children().filter_map(Keyword::cast)) + .flat_map(|section| section.syntax.children().filter_map(Keyword::cast)) } - /// Returns the value in `#+TITLE` + /// Returns the value in top-level `#+TITLE` + /// + /// Multiple `#+TITLE` are joined with spaces. + /// + /// Returns `None` if file doesn't contain `#+TITLE` /// /// ```rust /// use orgize::{Org, ast::Document}; diff --git a/src/ast/generate.js b/src/ast/generate.js index 81bbc4f..26cfd82 100644 --- a/src/ast/generate.js +++ b/src/ast/generate.js @@ -6,6 +6,7 @@ const nodes = [ first_child: [ ["section", "Section"], ["first_headline", "Headline"], + ["properties", "PropertyDrawer"], ], last_child: [["last_headline", "Headline"]], children: [["headlines", "Headline"]], diff --git a/src/ast/generated.rs b/src/ast/generated.rs index 1bfd489..a34d493 100644 --- a/src/ast/generated.rs +++ b/src/ast/generated.rs @@ -57,6 +57,9 @@ impl Document { pub fn first_headline(&self) -> Option { support::child(&self.syntax) } + pub fn properties(&self) -> Option { + support::child(&self.syntax) + } pub fn last_headline(&self) -> Option { super::last_child(&self.syntax) } diff --git a/src/syntax/document.rs b/src/syntax/document.rs index 7f5aa9f..b32fc08 100644 --- a/src/syntax/document.rs +++ b/src/syntax/document.rs @@ -2,6 +2,7 @@ use nom::{combinator::opt, IResult}; use super::{ combinator::{blank_lines, node, GreenElement}, + drawer::property_drawer_node, headline::{headline_node, section_node}, input::Input, SyntaxKind::*, @@ -16,8 +17,17 @@ pub fn document_node(input: Input) -> IResult { } fn document_node_base(input: Input) -> IResult { + if input.is_empty() { + return Ok((input, node(DOCUMENT, []))); + } + let mut children = vec![]; + let (input, property_drawer) = opt(property_drawer_node)(input)?; + if let Some(property_drawer) = property_drawer { + children.push(property_drawer); + } + let (input, pre_blank) = blank_lines(input)?; children.extend(pre_blank); diff --git a/src/syntax/drawer.rs b/src/syntax/drawer.rs index 67b51b6..aee47e0 100644 --- a/src/syntax/drawer.rs +++ b/src/syntax/drawer.rs @@ -87,7 +87,7 @@ fn drawer_node_base(input: Input) -> IResult { fn property_drawer_node_base(input: Input) -> IResult { let (input, (begin, name)) = drawer_begin_node(input)?; - if name != "PROPERTIES" { + if !name.eq_ignore_ascii_case("properties") { return Err(nom::Err::Error(())); } @@ -97,8 +97,10 @@ fn property_drawer_node_base(input: Input) -> IResult { children.extend(&mut it); let (input, _) = it.finish()?; let (input, end) = drawer_end_node(input)?; + let (input, post_blank) = blank_lines(input)?; children.push(end); + children.extend(post_blank); Ok((input, node(PROPERTY_DRAWER, children))) }