feat: support top-level properties drawer (#78)

This commit is contained in:
PoiScript 2024-05-09 16:41:43 +08:00
parent 2f31fd4b10
commit 9b8aec02a4
No known key found for this signature in database
GPG key ID: 22C2B1249D99985E
5 changed files with 31 additions and 9 deletions

View file

@ -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::<Document>().unwrap();
/// assert_eq!(doc.keywords().count(), 4);
/// ```
pub fn keywords(&self) -> impl Iterator<Item = Keyword> {
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};

View file

@ -6,6 +6,7 @@ const nodes = [
first_child: [
["section", "Section"],
["first_headline", "Headline"],
["properties", "PropertyDrawer"],
],
last_child: [["last_headline", "Headline"]],
children: [["headlines", "Headline"]],

View file

@ -57,6 +57,9 @@ impl Document {
pub fn first_headline(&self) -> Option<Headline> {
support::child(&self.syntax)
}
pub fn properties(&self) -> Option<PropertyDrawer> {
support::child(&self.syntax)
}
pub fn last_headline(&self) -> Option<Headline> {
super::last_child(&self.syntax)
}

View file

@ -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<Input, GreenElement, ()> {
}
fn document_node_base(input: Input) -> IResult<Input, GreenElement, ()> {
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);

View file

@ -87,7 +87,7 @@ fn drawer_node_base(input: Input) -> IResult<Input, GreenElement, ()> {
fn property_drawer_node_base(input: Input) -> IResult<Input, GreenElement, ()> {
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<Input, GreenElement, ()> {
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)))
}