55 Commits

Author SHA1 Message Date
Tom Alexander
e673aa862e Publish version 0.1.9.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-03 00:18:00 -04:00
Tom Alexander
3b6659c5fd Merge branch 'table_properties'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-03 00:14:21 -04:00
Tom Alexander
68a3f8b87e Fix table rule row detection. 2023-10-03 00:13:15 -04:00
Tom Alexander
b1244de1dc Compare row type. 2023-10-03 00:03:58 -04:00
Tom Alexander
e5a402ee1b Compare type and value.
All checks were successful
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
Since we only support org-mode tables, type is always org. Value seems to always be nil, not sure why.
2023-10-02 23:57:17 -04:00
Tom Alexander
d4a2ad4a7f Merge branch 'node_property_properties'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-02 23:47:11 -04:00
Tom Alexander
3d1b2713ed Compare key and value. 2023-10-02 23:45:31 -04:00
Tom Alexander
60bec4695b Merge branch 'drawer_properties' 2023-10-02 23:38:34 -04:00
Tom Alexander
d992947ff1 Compare name. 2023-10-02 23:34:06 -04:00
Tom Alexander
76fb24d1d1 Merge branch 'comment_properties'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-02 23:30:29 -04:00
Tom Alexander
b56318fbe4 Add TODO comment. 2023-10-02 23:29:58 -04:00
Tom Alexander
8169499de3 Compare value. 2023-10-02 23:28:32 -04:00
Tom Alexander
29d9e76545 Merge branch 'footnote_definition_properties' 2023-10-02 22:50:26 -04:00
Tom Alexander
4d356b855e Compare label. 2023-10-02 22:48:54 -04:00
Tom Alexander
ae66d1bd89 Fix tracing build.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-02 22:45:25 -04:00
Tom Alexander
c551938904 Merge branch 'dynamic_block_properties' 2023-10-02 22:43:26 -04:00
Tom Alexander
0fb80e3fee Compare name and parameters. 2023-10-02 22:41:56 -04:00
Tom Alexander
590e7fba0e Merge branch 'greater_block_properties'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has failed
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-02 22:37:11 -04:00
Tom Alexander
4a72747dc9 Compare name and parameters. 2023-10-02 22:33:00 -04:00
Tom Alexander
2352636672 Split GreaterBlock into CenterBlock, QuoteBlock, and SpecialBlock.
Center and quote blocks do not have parameters nor do they store their name so I am separating them out.
2023-10-02 22:33:00 -04:00
Tom Alexander
36217f5704 Do not capture trailing whitespace in parameters. 2023-10-02 21:14:07 -04:00
Tom Alexander
0654b676f7 Merge branch 'planning_properties'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has failed
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-02 21:00:08 -04:00
Tom Alexander
a80d171e4d Bubble up planning variables to the headline. 2023-10-02 20:37:46 -04:00
Tom Alexander
2e1a946ac9 Compare scheduled, deadline, and closed. 2023-10-02 20:25:08 -04:00
Tom Alexander
01c2f1bf66 Add a test for a timestamp with a malformed repeater. 2023-10-02 20:04:39 -04:00
Tom Alexander
be483110ef Merge branch 'timestamp_properties'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-02 20:00:29 -04:00
Tom Alexander
94401dcf00 Allow REST despite no TIME. 2023-10-02 19:51:29 -04:00
Tom Alexander
2b5df83956 Format the code. 2023-10-02 19:24:47 -04:00
Tom Alexander
d53b9e1e1f Fix get_property.
This was returning the error when a token was not an atom whereas we only wanted to check to see if it was the atom nil.
2023-10-02 19:22:35 -04:00
Tom Alexander
5c929ffc13 Fix repeater type.
I had Cumulative and CatchUp backwards.
2023-10-02 19:18:25 -04:00
Tom Alexander
bc3224be7a Revert the rest_end functions. 2023-10-02 19:09:20 -04:00
Tom Alexander
54c66fb4d6 Change get_property to allow absent values.
We're returning an Option<> anyway so might as well handle absent values.
2023-10-02 19:07:12 -04:00
Tom Alexander
6a8ae9d838 Compare warning delay and repeater. 2023-10-02 18:58:30 -04:00
Tom Alexander
512432c5f0 Do not allow time range timestamps with REST on the first TIME. 2023-10-02 17:51:33 -04:00
Tom Alexander
890cd3e4fd Compare start/end time. 2023-10-02 17:17:05 -04:00
Tom Alexander
9846cde2f0 Trim whitespace from raw value. 2023-10-02 16:32:33 -04:00
Tom Alexander
dec3242e72 Implement the Time struct. 2023-10-02 16:24:51 -04:00
Tom Alexander
a8a34e2d9c Compare date start/end. 2023-10-02 16:16:19 -04:00
Tom Alexander
c55fae86f8 Improve lifetimes for get_property_numeric. 2023-10-02 15:51:29 -04:00
Tom Alexander
e7ec23af3d Move the Date struct into types and implement a get_property_numeric. 2023-10-02 15:49:51 -04:00
Tom Alexander
10ae36a419 Implement date types with basic validation. 2023-10-02 15:10:39 -04:00
Tom Alexander
ecdfd7087f Compare raw-value. 2023-10-02 14:45:20 -04:00
Tom Alexander
3ed9b552e2 Compare range type. 2023-10-02 14:35:45 -04:00
Tom Alexander
d04c8c832c Compare timestamp type. 2023-10-02 13:40:37 -04:00
Tom Alexander
9575ef30ac Fix compilation.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-02 13:19:43 -04:00
Tom Alexander
06ecf41663 Add notes about the fields for timestamps. 2023-10-02 13:19:43 -04:00
Tom Alexander
10d03fd432 Merge branch 'standard_ast_node'
Some checks failed
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-test Build rust-test has succeeded
2023-10-02 13:14:22 -04:00
Tom Alexander
a62c3fc522 Move AstNode into the types crate.
Now that it is used for more than just iteration, it makes sense to promote it to the types crate.
2023-10-02 13:10:45 -04:00
Tom Alexander
25f664e69e Fix warnings. 2023-10-02 13:05:51 -04:00
Tom Alexander
52e0d305aa Remove compare_element and compare_object. 2023-10-02 13:05:29 -04:00
Tom Alexander
418c5c1ce8 Implement the traits for all ast node types. 2023-10-02 12:53:23 -04:00
Tom Alexander
ecd523fa8f Fix lifetimes in the compare functions. 2023-10-02 12:36:09 -04:00
Tom Alexander
c0555dec0b Fix lifetimes for DiffEntry/DiffResult. 2023-10-02 12:28:48 -04:00
Tom Alexander
1b788f3f21 Fix lifetimes on StandardProperties. 2023-10-02 12:11:05 -04:00
Tom Alexander
b3382c66cd Fix lifetimes on ElispFact.
This was listed as a yellow flag on https://quinedot.github.io/rust-learning/pf-shared-nested.html.
2023-10-02 12:11:05 -04:00
36 changed files with 2191 additions and 824 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "organic" name = "organic"
version = "0.1.8" version = "0.1.9"
authors = ["Tom Alexander <tom@fizz.buzz>"] authors = ["Tom Alexander <tom@fizz.buzz>"]
description = "An org-mode parser." description = "An org-mode parser."
edition = "2021" edition = "2021"

View File

@@ -0,0 +1,3 @@
#+begin_defun foo bar baz
lorem
#+end_defun

View File

@@ -1,4 +1,5 @@
# Comment # Comment
#
# indented line # indented line
# At the top of the file # At the top of the file

View File

@@ -0,0 +1,7 @@
# All the marks for repeater and warning delay
[1970-01-01 Thu 8:15-13:15foo +1h -2h]
[1970-01-01 Thu 8:15-13:15foo ++1d -2d]
[1970-01-01 Thu 8:15-13:15foo .+1w -2w]
[1970-01-01 Thu 8:15-13:15foo +1m --2m]
[1970-01-01 Thu 8:15-13:15foo ++1y --2y]
[1970-01-01 Thu 8:15-13:15foo .+1d --2h]

View File

@@ -2,13 +2,17 @@
<%%(foo bar baz)> <%%(foo bar baz)>
# active # active
<1970-01-01 Thu 8:15rest +1w -1d> <1970-01-01 Thu 8:15rest +1w -1d>
# Any value for "REST" in the first timestamp makes this a regular timestamp rather than a time range.
<1970-01-01 Thu 8:15rest-13:15otherrest +1w -1d>
# inactive # inactive
[1970-01-01 Thu 8:15rest +1w -1d] [1970-01-01 Thu 8:15rest +1w -1d]
# Any value for "REST" in the first timestamp makes this a regular timestamp rather than a time range.
[1970-01-01 Thu 8:15rest-13:15otherrest +1w -1d]
# active date range # active date range
<1970-01-01 Thu 8:15rest +1w -1d>--<1970-01-01 Thu 8:15rest +1w -1d> <1970-01-01 Thu 8:15rest +1w -1d>--<1970-01-01 Thu 8:15rest +1w -1d>
# active time range # active time range
<1970-01-01 Thu 8:15rest-13:15otherrest +1w -1d> <1970-01-01 Thu 8:15-13:15otherrest +1w -1d>
# inactive date range # inactive date range
[1970-01-01 Thu 8:15rest +1w -1d]--[1970-01-01 Thu 8:15rest +1w -1d] [1970-01-01 Thu 8:15rest +1w -1d]--[1970-01-01 Thu 8:15rest +1w -1d]
# inactive time range # inactive time range
[1970-01-01 Thu 8:15rest-13:15otherrest +1w -1d] [1970-01-01 Thu 8:15-13:15otherrest +1w -1d]

View File

@@ -0,0 +1,2 @@
# This should be a malformed timestamp according to the current org-mode documentation but it is accepted anyway (with no repeater).
<1970-01-01 Thu ++y>

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,10 @@
use std::borrow::Cow; use std::borrow::Cow;
use crate::types::AngleLink; use crate::types::AngleLink;
use crate::types::AstNode;
use crate::types::BabelCall; use crate::types::BabelCall;
use crate::types::Bold; use crate::types::Bold;
use crate::types::CenterBlock;
use crate::types::Citation; use crate::types::Citation;
use crate::types::CitationReference; use crate::types::CitationReference;
use crate::types::Clock; use crate::types::Clock;
@@ -21,7 +23,6 @@ use crate::types::ExportSnippet;
use crate::types::FixedWidthArea; use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition; use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference; use crate::types::FootnoteReference;
use crate::types::GreaterBlock;
use crate::types::Heading; use crate::types::Heading;
use crate::types::HorizontalRule; use crate::types::HorizontalRule;
use crate::types::InlineBabelCall; use crate::types::InlineBabelCall;
@@ -41,10 +42,12 @@ use crate::types::PlainListItem;
use crate::types::PlainText; use crate::types::PlainText;
use crate::types::Planning; use crate::types::Planning;
use crate::types::PropertyDrawer; use crate::types::PropertyDrawer;
use crate::types::QuoteBlock;
use crate::types::RadioLink; use crate::types::RadioLink;
use crate::types::RadioTarget; use crate::types::RadioTarget;
use crate::types::RegularLink; use crate::types::RegularLink;
use crate::types::Section; use crate::types::Section;
use crate::types::SpecialBlock;
use crate::types::SrcBlock; use crate::types::SrcBlock;
use crate::types::StatisticsCookie; use crate::types::StatisticsCookie;
use crate::types::StrikeThrough; use crate::types::StrikeThrough;
@@ -60,25 +63,92 @@ use crate::types::Verbatim;
use crate::types::VerseBlock; use crate::types::VerseBlock;
pub(crate) trait ElispFact<'s> { pub(crate) trait ElispFact<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str>; fn get_elisp_name<'b>(&'b self) -> Cow<'s, str>;
} }
pub(crate) trait GetElispFact<'s> { pub(crate) trait GetElispFact<'s> {
fn get_elisp_fact(&'s self) -> &'s dyn ElispFact<'s>; fn get_elisp_fact<'b>(&'b self) -> &'b dyn ElispFact<'s>;
} }
impl<'s, I: ElispFact<'s>> GetElispFact<'s> for I { impl<'s, I: ElispFact<'s>> GetElispFact<'s> for I {
fn get_elisp_fact(&'s self) -> &'s dyn ElispFact<'s> { fn get_elisp_fact<'b>(&'b self) -> &'b dyn ElispFact<'s> {
self self
} }
} }
impl<'r, 's> GetElispFact<'s> for AstNode<'r, 's> {
fn get_elisp_fact<'b>(&'b self) -> &'b dyn ElispFact<'s> {
match self {
AstNode::Document(inner) => *inner,
AstNode::Heading(inner) => *inner,
AstNode::Section(inner) => *inner,
AstNode::Paragraph(inner) => *inner,
AstNode::PlainList(inner) => *inner,
AstNode::PlainListItem(inner) => *inner,
AstNode::CenterBlock(inner) => *inner,
AstNode::QuoteBlock(inner) => *inner,
AstNode::SpecialBlock(inner) => *inner,
AstNode::DynamicBlock(inner) => *inner,
AstNode::FootnoteDefinition(inner) => *inner,
AstNode::Comment(inner) => *inner,
AstNode::Drawer(inner) => *inner,
AstNode::PropertyDrawer(inner) => *inner,
AstNode::NodeProperty(inner) => *inner,
AstNode::Table(inner) => *inner,
AstNode::TableRow(inner) => *inner,
AstNode::VerseBlock(inner) => *inner,
AstNode::CommentBlock(inner) => *inner,
AstNode::ExampleBlock(inner) => *inner,
AstNode::ExportBlock(inner) => *inner,
AstNode::SrcBlock(inner) => *inner,
AstNode::Clock(inner) => *inner,
AstNode::DiarySexp(inner) => *inner,
AstNode::Planning(inner) => *inner,
AstNode::FixedWidthArea(inner) => *inner,
AstNode::HorizontalRule(inner) => *inner,
AstNode::Keyword(inner) => *inner,
AstNode::BabelCall(inner) => *inner,
AstNode::LatexEnvironment(inner) => *inner,
AstNode::Bold(inner) => *inner,
AstNode::Italic(inner) => *inner,
AstNode::Underline(inner) => *inner,
AstNode::StrikeThrough(inner) => *inner,
AstNode::Code(inner) => *inner,
AstNode::Verbatim(inner) => *inner,
AstNode::PlainText(inner) => *inner,
AstNode::RegularLink(inner) => *inner,
AstNode::RadioLink(inner) => *inner,
AstNode::RadioTarget(inner) => *inner,
AstNode::PlainLink(inner) => *inner,
AstNode::AngleLink(inner) => *inner,
AstNode::OrgMacro(inner) => *inner,
AstNode::Entity(inner) => *inner,
AstNode::LatexFragment(inner) => *inner,
AstNode::ExportSnippet(inner) => *inner,
AstNode::FootnoteReference(inner) => *inner,
AstNode::Citation(inner) => *inner,
AstNode::CitationReference(inner) => *inner,
AstNode::InlineBabelCall(inner) => *inner,
AstNode::InlineSourceBlock(inner) => *inner,
AstNode::LineBreak(inner) => *inner,
AstNode::Target(inner) => *inner,
AstNode::StatisticsCookie(inner) => *inner,
AstNode::Subscript(inner) => *inner,
AstNode::Superscript(inner) => *inner,
AstNode::TableCell(inner) => *inner,
AstNode::Timestamp(inner) => *inner,
}
}
}
impl<'s> GetElispFact<'s> for Element<'s> { impl<'s> GetElispFact<'s> for Element<'s> {
fn get_elisp_fact(&'s self) -> &'s dyn ElispFact<'s> { fn get_elisp_fact<'b>(&'b self) -> &'b dyn ElispFact<'s> {
match self { match self {
Element::Paragraph(inner) => inner, Element::Paragraph(inner) => inner,
Element::PlainList(inner) => inner, Element::PlainList(inner) => inner,
Element::GreaterBlock(inner) => inner, Element::CenterBlock(inner) => inner,
Element::QuoteBlock(inner) => inner,
Element::SpecialBlock(inner) => inner,
Element::DynamicBlock(inner) => inner, Element::DynamicBlock(inner) => inner,
Element::FootnoteDefinition(inner) => inner, Element::FootnoteDefinition(inner) => inner,
Element::Comment(inner) => inner, Element::Comment(inner) => inner,
@@ -103,7 +173,7 @@ impl<'s> GetElispFact<'s> for Element<'s> {
} }
impl<'s> GetElispFact<'s> for Object<'s> { impl<'s> GetElispFact<'s> for Object<'s> {
fn get_elisp_fact(&'s self) -> &'s dyn ElispFact<'s> { fn get_elisp_fact<'b>(&'b self) -> &'b dyn ElispFact<'s> {
match self { match self {
Object::Bold(inner) => inner, Object::Bold(inner) => inner,
Object::Italic(inner) => inner, Object::Italic(inner) => inner,
@@ -137,337 +207,345 @@ impl<'s> GetElispFact<'s> for Object<'s> {
} }
impl<'s> ElispFact<'s> for Document<'s> { impl<'s> ElispFact<'s> for Document<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"org-data".into() "org-data".into()
} }
} }
impl<'s> ElispFact<'s> for Section<'s> { impl<'s> ElispFact<'s> for Section<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"section".into() "section".into()
} }
} }
impl<'s> ElispFact<'s> for Heading<'s> { impl<'s> ElispFact<'s> for Heading<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"headline".into() "headline".into()
} }
} }
impl<'s> ElispFact<'s> for PlainList<'s> { impl<'s> ElispFact<'s> for PlainList<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"plain-list".into() "plain-list".into()
} }
} }
impl<'s> ElispFact<'s> for PlainListItem<'s> { impl<'s> ElispFact<'s> for PlainListItem<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"item".into() "item".into()
} }
} }
impl<'s> ElispFact<'s> for GreaterBlock<'s> { impl<'s> ElispFact<'s> for CenterBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
match self.name.to_lowercase().as_str() { "center-block".into()
"center" => "center-block".into(), }
"quote" => "quote-block".into(), }
_ => "special-block".into(),
} impl<'s> ElispFact<'s> for QuoteBlock<'s> {
fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"quote-block".into()
}
}
impl<'s> ElispFact<'s> for SpecialBlock<'s> {
fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"special-block".into()
} }
} }
impl<'s> ElispFact<'s> for DynamicBlock<'s> { impl<'s> ElispFact<'s> for DynamicBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"dynamic-block".into() "dynamic-block".into()
} }
} }
impl<'s> ElispFact<'s> for FootnoteDefinition<'s> { impl<'s> ElispFact<'s> for FootnoteDefinition<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"footnote-definition".into() "footnote-definition".into()
} }
} }
impl<'s> ElispFact<'s> for Drawer<'s> { impl<'s> ElispFact<'s> for Drawer<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"drawer".into() "drawer".into()
} }
} }
impl<'s> ElispFact<'s> for PropertyDrawer<'s> { impl<'s> ElispFact<'s> for PropertyDrawer<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"property-drawer".into() "property-drawer".into()
} }
} }
impl<'s> ElispFact<'s> for NodeProperty<'s> { impl<'s> ElispFact<'s> for NodeProperty<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"node-property".into() "node-property".into()
} }
} }
impl<'s> ElispFact<'s> for Table<'s> { impl<'s> ElispFact<'s> for Table<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"table".into() "table".into()
} }
} }
impl<'s> ElispFact<'s> for TableRow<'s> { impl<'s> ElispFact<'s> for TableRow<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"table-row".into() "table-row".into()
} }
} }
impl<'s> ElispFact<'s> for Paragraph<'s> { impl<'s> ElispFact<'s> for Paragraph<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"paragraph".into() "paragraph".into()
} }
} }
impl<'s> ElispFact<'s> for TableCell<'s> { impl<'s> ElispFact<'s> for TableCell<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"table-cell".into() "table-cell".into()
} }
} }
impl<'s> ElispFact<'s> for Comment<'s> { impl<'s> ElispFact<'s> for Comment<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"comment".into() "comment".into()
} }
} }
impl<'s> ElispFact<'s> for VerseBlock<'s> { impl<'s> ElispFact<'s> for VerseBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"verse-block".into() "verse-block".into()
} }
} }
impl<'s> ElispFact<'s> for CommentBlock<'s> { impl<'s> ElispFact<'s> for CommentBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"comment-block".into() "comment-block".into()
} }
} }
impl<'s> ElispFact<'s> for ExampleBlock<'s> { impl<'s> ElispFact<'s> for ExampleBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"example-block".into() "example-block".into()
} }
} }
impl<'s> ElispFact<'s> for ExportBlock<'s> { impl<'s> ElispFact<'s> for ExportBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"export-block".into() "export-block".into()
} }
} }
impl<'s> ElispFact<'s> for SrcBlock<'s> { impl<'s> ElispFact<'s> for SrcBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"src-block".into() "src-block".into()
} }
} }
impl<'s> ElispFact<'s> for Clock<'s> { impl<'s> ElispFact<'s> for Clock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"clock".into() "clock".into()
} }
} }
impl<'s> ElispFact<'s> for DiarySexp<'s> { impl<'s> ElispFact<'s> for DiarySexp<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"diary-sexp".into() "diary-sexp".into()
} }
} }
impl<'s> ElispFact<'s> for Planning<'s> { impl<'s> ElispFact<'s> for Planning<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"planning".into() "planning".into()
} }
} }
impl<'s> ElispFact<'s> for FixedWidthArea<'s> { impl<'s> ElispFact<'s> for FixedWidthArea<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"fixed-width".into() "fixed-width".into()
} }
} }
impl<'s> ElispFact<'s> for HorizontalRule<'s> { impl<'s> ElispFact<'s> for HorizontalRule<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"horizontal-rule".into() "horizontal-rule".into()
} }
} }
impl<'s> ElispFact<'s> for Keyword<'s> { impl<'s> ElispFact<'s> for Keyword<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"keyword".into() "keyword".into()
} }
} }
impl<'s> ElispFact<'s> for BabelCall<'s> { impl<'s> ElispFact<'s> for BabelCall<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"babel-call".into() "babel-call".into()
} }
} }
impl<'s> ElispFact<'s> for LatexEnvironment<'s> { impl<'s> ElispFact<'s> for LatexEnvironment<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"latex-environment".into() "latex-environment".into()
} }
} }
impl<'s> ElispFact<'s> for Bold<'s> { impl<'s> ElispFact<'s> for Bold<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"bold".into() "bold".into()
} }
} }
impl<'s> ElispFact<'s> for Italic<'s> { impl<'s> ElispFact<'s> for Italic<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"italic".into() "italic".into()
} }
} }
impl<'s> ElispFact<'s> for Underline<'s> { impl<'s> ElispFact<'s> for Underline<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"underline".into() "underline".into()
} }
} }
impl<'s> ElispFact<'s> for StrikeThrough<'s> { impl<'s> ElispFact<'s> for StrikeThrough<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"strike-through".into() "strike-through".into()
} }
} }
impl<'s> ElispFact<'s> for Code<'s> { impl<'s> ElispFact<'s> for Code<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"code".into() "code".into()
} }
} }
impl<'s> ElispFact<'s> for Verbatim<'s> { impl<'s> ElispFact<'s> for Verbatim<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"verbatim".into() "verbatim".into()
} }
} }
impl<'s> ElispFact<'s> for RegularLink<'s> { impl<'s> ElispFact<'s> for RegularLink<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"link".into() "link".into()
} }
} }
impl<'s> ElispFact<'s> for RadioLink<'s> { impl<'s> ElispFact<'s> for RadioLink<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"link".into() "link".into()
} }
} }
impl<'s> ElispFact<'s> for RadioTarget<'s> { impl<'s> ElispFact<'s> for RadioTarget<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"radio-target".into() "radio-target".into()
} }
} }
impl<'s> ElispFact<'s> for PlainLink<'s> { impl<'s> ElispFact<'s> for PlainLink<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"link".into() "link".into()
} }
} }
impl<'s> ElispFact<'s> for AngleLink<'s> { impl<'s> ElispFact<'s> for AngleLink<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"link".into() "link".into()
} }
} }
impl<'s> ElispFact<'s> for OrgMacro<'s> { impl<'s> ElispFact<'s> for OrgMacro<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"macro".into() "macro".into()
} }
} }
impl<'s> ElispFact<'s> for Entity<'s> { impl<'s> ElispFact<'s> for Entity<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"entity".into() "entity".into()
} }
} }
impl<'s> ElispFact<'s> for LatexFragment<'s> { impl<'s> ElispFact<'s> for LatexFragment<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"latex-fragment".into() "latex-fragment".into()
} }
} }
impl<'s> ElispFact<'s> for ExportSnippet<'s> { impl<'s> ElispFact<'s> for ExportSnippet<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"export-snippet".into() "export-snippet".into()
} }
} }
impl<'s> ElispFact<'s> for FootnoteReference<'s> { impl<'s> ElispFact<'s> for FootnoteReference<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"footnote-reference".into() "footnote-reference".into()
} }
} }
impl<'s> ElispFact<'s> for Citation<'s> { impl<'s> ElispFact<'s> for Citation<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"citation".into() "citation".into()
} }
} }
impl<'s> ElispFact<'s> for CitationReference<'s> { impl<'s> ElispFact<'s> for CitationReference<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"citation-reference".into() "citation-reference".into()
} }
} }
impl<'s> ElispFact<'s> for InlineBabelCall<'s> { impl<'s> ElispFact<'s> for InlineBabelCall<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"inline-babel-call".into() "inline-babel-call".into()
} }
} }
impl<'s> ElispFact<'s> for InlineSourceBlock<'s> { impl<'s> ElispFact<'s> for InlineSourceBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"inline-src-block".into() "inline-src-block".into()
} }
} }
impl<'s> ElispFact<'s> for LineBreak<'s> { impl<'s> ElispFact<'s> for LineBreak<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"line-break".into() "line-break".into()
} }
} }
impl<'s> ElispFact<'s> for Target<'s> { impl<'s> ElispFact<'s> for Target<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"target".into() "target".into()
} }
} }
impl<'s> ElispFact<'s> for StatisticsCookie<'s> { impl<'s> ElispFact<'s> for StatisticsCookie<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"statistics-cookie".into() "statistics-cookie".into()
} }
} }
impl<'s> ElispFact<'s> for Subscript<'s> { impl<'s> ElispFact<'s> for Subscript<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"subscript".into() "subscript".into()
} }
} }
impl<'s> ElispFact<'s> for Superscript<'s> { impl<'s> ElispFact<'s> for Superscript<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"superscript".into() "superscript".into()
} }
} }
impl<'s> ElispFact<'s> for Timestamp<'s> { impl<'s> ElispFact<'s> for Timestamp<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
"timestamp".into() "timestamp".into()
} }
} }
impl<'s> ElispFact<'s> for PlainText<'s> { impl<'s> ElispFact<'s> for PlainText<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> { fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
// plain text from upstream emacs does not actually have a name but this is included here to make rendering the status diff easier. // plain text from upstream emacs does not actually have a name but this is included here to make rendering the status diff easier.
"plain-text".into() "plain-text".into()
} }

View File

@@ -1,3 +1,5 @@
use std::str::FromStr;
use super::elisp_fact::GetElispFact; use super::elisp_fact::GetElispFact;
use super::sexp::Token; use super::sexp::Token;
use crate::compare::sexp::unquote; use crate::compare::sexp::unquote;
@@ -16,9 +18,9 @@ fn is_slice_of(parent: &str, child: &str) -> bool {
/// Get the byte offset into source that the rust object exists at. /// Get the byte offset into source that the rust object exists at.
/// ///
/// These offsets are zero-based unlike the elisp ones. /// These offsets are zero-based unlike the elisp ones.
fn get_rust_byte_offsets<'s, S: StandardProperties<'s> + ?Sized>( fn get_rust_byte_offsets<'b, 's, S: StandardProperties<'s> + ?Sized>(
original_document: &'s str, original_document: &'s str,
rust_ast_node: &'s S, rust_ast_node: &'b S,
) -> (usize, usize) { ) -> (usize, usize) {
let rust_object_source = rust_ast_node.get_source(); let rust_object_source = rust_ast_node.get_source();
debug_assert!(is_slice_of(original_document, rust_object_source)); debug_assert!(is_slice_of(original_document, rust_object_source));
@@ -28,20 +30,21 @@ fn get_rust_byte_offsets<'s, S: StandardProperties<'s> + ?Sized>(
} }
pub(crate) fn compare_standard_properties< pub(crate) fn compare_standard_properties<
'b,
's, 's,
S: GetStandardProperties<'s> + GetElispFact<'s> + ?Sized, S: GetStandardProperties<'s> + GetElispFact<'s> + ?Sized,
>( >(
original_document: &'s str, original_document: &'s str,
emacs: &'s Token<'s>, emacs: &'b Token<'s>,
rust: &'s S, rust: &'b S,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
assert_name(emacs, rust.get_elisp_fact().get_elisp_name())?; assert_name(emacs, rust.get_elisp_fact().get_elisp_name())?;
assert_bounds(original_document, emacs, rust.get_standard_properties())?; assert_bounds(original_document, emacs, rust.get_standard_properties())?;
Ok(()) Ok(())
} }
pub(crate) fn assert_name<'s, S: AsRef<str>>( pub(crate) fn assert_name<'b, 's, S: AsRef<str>>(
emacs: &'s Token<'s>, emacs: &'b Token<'s>,
name: S, name: S,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let name = name.as_ref(); let name = name.as_ref();
@@ -63,10 +66,10 @@ pub(crate) fn assert_name<'s, S: AsRef<str>>(
/// Assert that the character ranges defined by upstream org-mode's :standard-properties match the slices in Organic's StandardProperties. /// Assert that the character ranges defined by upstream org-mode's :standard-properties match the slices in Organic's StandardProperties.
/// ///
/// This does **not** handle plain text because plain text is a special case. /// This does **not** handle plain text because plain text is a special case.
pub(crate) fn assert_bounds<'s, S: StandardProperties<'s> + ?Sized>( pub(crate) fn assert_bounds<'b, 's, S: StandardProperties<'s> + ?Sized>(
original_document: &'s str, original_document: &'s str,
emacs: &'s Token<'s>, emacs: &'b Token<'s>,
rust: &'s S, rust: &'b S,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let standard_properties = get_emacs_standard_properties(emacs)?; // 1-based let standard_properties = get_emacs_standard_properties(emacs)?; // 1-based
let (begin, end) = ( let (begin, end) = (
@@ -99,8 +102,8 @@ struct EmacsStandardProperties {
post_blank: Option<usize>, post_blank: Option<usize>,
} }
fn get_emacs_standard_properties<'s>( fn get_emacs_standard_properties<'b, 's>(
emacs: &'s Token<'s>, emacs: &'b Token<'s>,
) -> Result<EmacsStandardProperties, Box<dyn std::error::Error>> { ) -> Result<EmacsStandardProperties, Box<dyn std::error::Error>> {
let children = emacs.as_list()?; let children = emacs.as_list()?;
let attributes_child = children let attributes_child = children
@@ -169,34 +172,30 @@ fn maybe_token_to_usize(
/// Get a named property from the emacs token. /// Get a named property from the emacs token.
/// ///
/// Returns Ok(None) if value is nil. /// Returns Ok(None) if value is nil or absent.
/// pub(crate) fn get_property<'b, 's, 'x>(
/// Returns error if the attribute is not specified on the token at all. emacs: &'b Token<'s>,
pub(crate) fn get_property<'s, 'x>(
emacs: &'s Token<'s>,
key: &'x str, key: &'x str,
) -> Result<Option<&'s Token<'s>>, Box<dyn std::error::Error>> { ) -> Result<Option<&'b Token<'s>>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?; let children = emacs.as_list()?;
let attributes_child = children let attributes_child = children
.iter() .iter()
.nth(1) .nth(1)
.ok_or("Should have an attributes child.")?; .ok_or("Should have an attributes child.")?;
let attributes_map = attributes_child.as_map()?; let attributes_map = attributes_child.as_map()?;
let prop = attributes_map let prop = attributes_map.get(key).map(|token| *token);
.get(key) match prop.map(|token| token.as_atom()) {
.ok_or(format!("Missing {} attribute.", key))?; Some(Ok("nil")) => return Ok(None),
match prop.as_atom() {
Ok("nil") => return Ok(None),
_ => {} _ => {}
}; };
Ok(Some(*prop)) Ok(prop)
} }
/// Get a named property containing an unquoted atom from the emacs token. /// Get a named property containing an unquoted atom from the emacs token.
/// ///
/// Returns None if key is not found. /// Returns None if key is not found.
pub(crate) fn get_property_unquoted_atom<'s, 'x>( pub(crate) fn get_property_unquoted_atom<'b, 's, 'x>(
emacs: &'s Token<'s>, emacs: &'b Token<'s>,
key: &'x str, key: &'x str,
) -> Result<Option<&'s str>, Box<dyn std::error::Error>> { ) -> Result<Option<&'s str>, Box<dyn std::error::Error>> {
Ok(get_property(emacs, key)? Ok(get_property(emacs, key)?
@@ -207,8 +206,8 @@ pub(crate) fn get_property_unquoted_atom<'s, 'x>(
/// Get a named property containing an quoted string from the emacs token. /// Get a named property containing an quoted string from the emacs token.
/// ///
/// Returns None if key is not found. /// Returns None if key is not found.
pub(crate) fn get_property_quoted_string<'s, 'x>( pub(crate) fn get_property_quoted_string<'b, 's, 'x>(
emacs: &'s Token<'s>, emacs: &'b Token<'s>,
key: &'x str, key: &'x str,
) -> Result<Option<String>, Box<dyn std::error::Error>> { ) -> Result<Option<String>, Box<dyn std::error::Error>> {
Ok(get_property(emacs, key)? Ok(get_property(emacs, key)?
@@ -223,8 +222,8 @@ pub(crate) fn get_property_quoted_string<'s, 'x>(
/// This uses the elisp convention of nil == false, non-nil == true. /// This uses the elisp convention of nil == false, non-nil == true.
/// ///
/// Returns false if key is not found. /// Returns false if key is not found.
pub(crate) fn get_property_boolean<'s, 'x>( pub(crate) fn get_property_boolean<'b, 's, 'x>(
emacs: &'s Token<'s>, emacs: &'b Token<'s>,
key: &'x str, key: &'x str,
) -> Result<bool, Box<dyn std::error::Error>> { ) -> Result<bool, Box<dyn std::error::Error>> {
Ok(get_property(emacs, key)? Ok(get_property(emacs, key)?
@@ -233,3 +232,23 @@ pub(crate) fn get_property_boolean<'s, 'x>(
.unwrap_or("nil") .unwrap_or("nil")
!= "nil") != "nil")
} }
/// Get a named property containing an unquoted numeric value.
///
/// Returns None if key is not found.
pub(crate) fn get_property_numeric<'b, 's, 'x, N: FromStr>(
emacs: &'b Token<'s>,
key: &'x str,
) -> Result<Option<N>, Box<dyn std::error::Error + 's>>
where
<N as FromStr>::Err: std::error::Error,
<N as FromStr>::Err: 's,
{
let unparsed_string = get_property(emacs, key)?
.map(Token::as_atom)
.map_or(Ok(None), |r| r.map(Some))?;
let parsed_number = unparsed_string
.map(|val| val.parse::<N>())
.map_or(Ok(None), |r| r.map(Some))?;
Ok(parsed_number)
}

View File

@@ -9,6 +9,7 @@ pub enum CustomError<I> {
MyError(MyError<&'static str>), MyError(MyError<&'static str>),
Nom(I, ErrorKind), Nom(I, ErrorKind),
IO(std::io::Error), IO(std::io::Error),
BoxedError(Box<dyn std::error::Error>),
} }
#[derive(Debug)] #[derive(Debug)]
@@ -36,3 +37,9 @@ impl<I> From<&'static str> for CustomError<I> {
CustomError::MyError(MyError(value)) CustomError::MyError(MyError(value))
} }
} }
impl<I> From<Box<dyn std::error::Error>> for CustomError<I> {
fn from(value: Box<dyn std::error::Error>) -> Self {
CustomError::BoxedError(value)
}
}

View File

@@ -1,7 +1,7 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use super::ast_node::AstNode;
use super::ast_node_iter::AstNodeIter; use super::ast_node_iter::AstNodeIter;
use crate::types::AstNode;
pub struct AllAstNodeIter<'r, 's> { pub struct AllAstNodeIter<'r, 's> {
root: Option<AstNode<'r, 's>>, root: Option<AstNode<'r, 's>>,
@@ -24,7 +24,9 @@ impl<'r, 's> Iterator for AllAstNodeIter<'r, 's> {
AstNodeIter::Paragraph(ref mut i) => i.next(), AstNodeIter::Paragraph(ref mut i) => i.next(),
AstNodeIter::PlainList(ref mut i) => i.next(), AstNodeIter::PlainList(ref mut i) => i.next(),
AstNodeIter::PlainListItem(ref mut i) => i.next(), AstNodeIter::PlainListItem(ref mut i) => i.next(),
AstNodeIter::GreaterBlock(ref mut i) => i.next(), AstNodeIter::CenterBlock(ref mut i) => i.next(),
AstNodeIter::QuoteBlock(ref mut i) => i.next(),
AstNodeIter::SpecialBlock(ref mut i) => i.next(),
AstNodeIter::DynamicBlock(ref mut i) => i.next(), AstNodeIter::DynamicBlock(ref mut i) => i.next(),
AstNodeIter::FootnoteDefinition(ref mut i) => i.next(), AstNodeIter::FootnoteDefinition(ref mut i) => i.next(),
AstNodeIter::Comment(ref mut i) => i.next(), AstNodeIter::Comment(ref mut i) => i.next(),

View File

@@ -1,12 +1,13 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use super::ast_node::AstNode;
use super::macros::children_iter; use super::macros::children_iter;
use super::macros::empty_iter; use super::macros::empty_iter;
use super::macros::multi_field_iter; use super::macros::multi_field_iter;
use crate::types::AngleLink; use crate::types::AngleLink;
use crate::types::AstNode;
use crate::types::BabelCall; use crate::types::BabelCall;
use crate::types::Bold; use crate::types::Bold;
use crate::types::CenterBlock;
use crate::types::Citation; use crate::types::Citation;
use crate::types::CitationReference; use crate::types::CitationReference;
use crate::types::Clock; use crate::types::Clock;
@@ -26,7 +27,6 @@ use crate::types::ExportSnippet;
use crate::types::FixedWidthArea; use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition; use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference; use crate::types::FootnoteReference;
use crate::types::GreaterBlock;
use crate::types::Heading; use crate::types::Heading;
use crate::types::HorizontalRule; use crate::types::HorizontalRule;
use crate::types::InlineBabelCall; use crate::types::InlineBabelCall;
@@ -46,10 +46,12 @@ use crate::types::PlainListItem;
use crate::types::PlainText; use crate::types::PlainText;
use crate::types::Planning; use crate::types::Planning;
use crate::types::PropertyDrawer; use crate::types::PropertyDrawer;
use crate::types::QuoteBlock;
use crate::types::RadioLink; use crate::types::RadioLink;
use crate::types::RadioTarget; use crate::types::RadioTarget;
use crate::types::RegularLink; use crate::types::RegularLink;
use crate::types::Section; use crate::types::Section;
use crate::types::SpecialBlock;
use crate::types::SrcBlock; use crate::types::SrcBlock;
use crate::types::StatisticsCookie; use crate::types::StatisticsCookie;
use crate::types::StrikeThrough; use crate::types::StrikeThrough;
@@ -78,7 +80,9 @@ pub(crate) enum AstNodeIter<'r, 's> {
Paragraph(ParagraphIter<'r, 's>), Paragraph(ParagraphIter<'r, 's>),
PlainList(PlainListIter<'r, 's>), PlainList(PlainListIter<'r, 's>),
PlainListItem(PlainListItemIter<'r, 's>), PlainListItem(PlainListItemIter<'r, 's>),
GreaterBlock(GreaterBlockIter<'r, 's>), CenterBlock(CenterBlockIter<'r, 's>),
QuoteBlock(QuoteBlockIter<'r, 's>),
SpecialBlock(SpecialBlockIter<'r, 's>),
DynamicBlock(DynamicBlockIter<'r, 's>), DynamicBlock(DynamicBlockIter<'r, 's>),
FootnoteDefinition(FootnoteDefinitionIter<'r, 's>), FootnoteDefinition(FootnoteDefinitionIter<'r, 's>),
Comment(CommentIter<'r, 's>), Comment(CommentIter<'r, 's>),
@@ -140,7 +144,9 @@ impl<'r, 's> AstNodeIter<'r, 's> {
AstNode::Paragraph(inner) => AstNodeIter::Paragraph(inner.into_iter()), AstNode::Paragraph(inner) => AstNodeIter::Paragraph(inner.into_iter()),
AstNode::PlainList(inner) => AstNodeIter::PlainList(inner.into_iter()), AstNode::PlainList(inner) => AstNodeIter::PlainList(inner.into_iter()),
AstNode::PlainListItem(inner) => AstNodeIter::PlainListItem(inner.into_iter()), AstNode::PlainListItem(inner) => AstNodeIter::PlainListItem(inner.into_iter()),
AstNode::GreaterBlock(inner) => AstNodeIter::GreaterBlock(inner.into_iter()), AstNode::CenterBlock(inner) => AstNodeIter::CenterBlock(inner.into_iter()),
AstNode::QuoteBlock(inner) => AstNodeIter::QuoteBlock(inner.into_iter()),
AstNode::SpecialBlock(inner) => AstNodeIter::SpecialBlock(inner.into_iter()),
AstNode::DynamicBlock(inner) => AstNodeIter::DynamicBlock(inner.into_iter()), AstNode::DynamicBlock(inner) => AstNodeIter::DynamicBlock(inner.into_iter()),
AstNode::FootnoteDefinition(inner) => { AstNode::FootnoteDefinition(inner) => {
AstNodeIter::FootnoteDefinition(inner.into_iter()) AstNodeIter::FootnoteDefinition(inner.into_iter())
@@ -232,8 +238,18 @@ multi_field_iter!(
std::slice::Iter<'r, Element<'s>> std::slice::Iter<'r, Element<'s>>
); );
children_iter!( children_iter!(
GreaterBlock<'s>, CenterBlock<'s>,
GreaterBlockIter, CenterBlockIter,
std::slice::Iter<'r, Element<'s>>
);
children_iter!(
QuoteBlock<'s>,
QuoteBlockIter,
std::slice::Iter<'r, Element<'s>>
);
children_iter!(
SpecialBlock<'s>,
SpecialBlockIter,
std::slice::Iter<'r, Element<'s>> std::slice::Iter<'r, Element<'s>>
); );
children_iter!( children_iter!(

View File

@@ -1,16 +1,3 @@
/// Write the implementation of From<> to convert a borrow of the type to an AstNode
macro_rules! to_ast_node {
($inp:ty, $enum:expr) => {
impl<'r, 's> From<$inp> for AstNode<'r, 's> {
fn from(value: $inp) -> Self {
$enum(value)
}
}
};
}
pub(crate) use to_ast_node;
/// Create iterators for ast nodes where it only has to iterate over children /// Create iterators for ast nodes where it only has to iterate over children
macro_rules! children_iter { macro_rules! children_iter {
($astnodetype:ty, $itertype:ident, $innertype:ty) => { ($astnodetype:ty, $itertype:ident, $innertype:ty) => {

View File

@@ -1,5 +1,3 @@
mod all_ast_node_iter; mod all_ast_node_iter;
mod ast_node;
mod ast_node_iter; mod ast_node_iter;
mod macros; mod macros;
pub(crate) use ast_node::AstNode;

View File

@@ -1,18 +1,19 @@
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt; use nom::combinator::recognize;
use nom::multi::many0; use nom::multi::many0;
use nom::multi::many_till;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::util::get_consumed; use super::util::get_consumed;
use super::util::org_line_ending;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::RefContext; use crate::context::RefContext;
@@ -38,15 +39,29 @@ pub(crate) fn comment<'b, 'g, 'r, 's>(
let parser_context = context.with_additional_node(&parser_context); let parser_context = context.with_additional_node(&parser_context);
let comment_line_matcher = parser_with_context!(comment_line)(&parser_context); let comment_line_matcher = parser_with_context!(comment_line)(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
let (remaining, _first_line) = comment_line_matcher(input)?; let (remaining, first_line) = comment_line_matcher(input)?;
let (remaining, _remaining_lines) = let (remaining, mut remaining_lines) =
many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?; many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
let mut value = Vec::with_capacity(remaining_lines.len() + 1);
let last_line = remaining_lines.pop();
if let Some(last_line) = last_line {
value.push(Into::<&str>::into(first_line));
value.extend(remaining_lines.into_iter().map(Into::<&str>::into));
let last_line = Into::<&str>::into(last_line);
// Trim the line ending from the final line.
value.push(&last_line[..(last_line.len() - 1)])
} else {
// Trim the line ending from the only line.
let only_line = Into::<&str>::into(first_line);
value.push(&only_line[..(only_line.len() - 1)])
}
Ok(( Ok((
remaining, remaining,
Comment { Comment {
source: source.into(), source: source.into(),
value,
}, },
)) ))
} }
@@ -57,14 +72,13 @@ fn comment_line<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?; start_of_line(input)?;
let (remaining, _indent) = space0(input)?; let (remaining, _) = tuple((space0, tag("#")))(input)?;
let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple(( if let Ok((remaining, line_break)) = org_line_ending(remaining) {
tag("#"), return Ok((remaining, line_break));
opt(tuple((space1, is_not("\r\n")))), }
alt((line_ending, eof)), let (remaining, _) = tag(" ")(remaining)?;
))(remaining)?; let (remaining, value) = recognize(many_till(anychar, org_line_ending))(remaining)?;
let source = get_consumed(input, remaining); Ok((remaining, value))
Ok((remaining, source))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@@ -20,9 +20,9 @@ use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::iter::AstNode;
use crate::parser::org_source::convert_error; use crate::parser::org_source::convert_error;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
use crate::types::AstNode;
use crate::types::Document; use crate::types::Document;
use crate::types::Object; use crate::types::Object;

View File

@@ -2,6 +2,7 @@ use nom::branch::alt;
use nom::bytes::complete::is_not; use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case; use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1; use nom::character::complete::space1;
@@ -9,6 +10,7 @@ use nom::combinator::consumed;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt; use nom::combinator::opt;
use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::multi::many0; use nom::multi::many0;
use nom::multi::many_till; use nom::multi::many_till;
@@ -47,10 +49,11 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>(
} }
start_of_line(input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, name, parameters, _ws)) = tuple(( let (remaining, (_, name, parameters, _, _)) = tuple((
recognize(tuple((tag_no_case("#+begin:"), space1))), recognize(tuple((tag_no_case("#+begin:"), space1))),
name, name,
opt(tuple((space1, parameters))), opt(tuple((space1, parameters))),
space0,
line_ending, line_ending,
))(remaining)?; ))(remaining)?;
let contexts = [ let contexts = [
@@ -108,7 +111,7 @@ fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { fn parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not("\r\n")(input) recognize(many_till(anychar, peek(tuple((space0, line_ending)))))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@@ -81,7 +81,7 @@ fn _element<'b, 'g, 'r, 's>(
let (remaining, mut affiliated_keywords) = many0(affiliated_keyword_matcher)(input)?; let (remaining, mut affiliated_keywords) = many0(affiliated_keyword_matcher)(input)?;
let (remaining, mut element) = match alt(( let (remaining, mut element) = match alt((
map(plain_list_matcher, Element::PlainList), map(plain_list_matcher, Element::PlainList),
map(greater_block_matcher, Element::GreaterBlock), greater_block_matcher,
map(dynamic_block_matcher, Element::DynamicBlock), map(dynamic_block_matcher, Element::DynamicBlock),
map(footnote_definition_matcher, Element::FootnoteDefinition), map(footnote_definition_matcher, Element::FootnoteDefinition),
map(comment_matcher, Element::Comment), map(comment_matcher, Element::Comment),

View File

@@ -1,6 +1,7 @@
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::is_not; use nom::bytes::complete::is_not;
use nom::bytes::complete::tag_no_case; use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1; use nom::character::complete::space1;
@@ -8,6 +9,8 @@ use nom::combinator::consumed;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt; use nom::combinator::opt;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many0; use nom::multi::many0;
use nom::multi::many_till; use nom::multi::many_till;
@@ -30,17 +33,18 @@ use crate::parser::util::blank_line;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::start_of_line; use crate::parser::util::start_of_line;
use crate::types::CenterBlock;
use crate::types::Element; use crate::types::Element;
use crate::types::GreaterBlock;
use crate::types::Paragraph; use crate::types::Paragraph;
use crate::types::QuoteBlock;
use crate::types::SetSource; use crate::types::SetSource;
use crate::types::SpecialBlock;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn greater_block<'b, 'g, 'r, 's>( pub(crate) fn greater_block<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, GreaterBlock<'s>> { ) -> Res<OrgSource<'s>, Element<'s>> {
// TODO: Do I need to differentiate between different greater block types.
start_of_line(input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, name)) = tuple(( let (remaining, (_begin, name)) = tuple((
@@ -52,22 +56,97 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
} }
}), }),
))(remaining)?; ))(remaining)?;
let context_name = match Into::<&str>::into(name).to_lowercase().as_str() { let name = Into::<&str>::into(name);
"center" => "center block".to_owned(), let (remaining, element) = match name.to_lowercase().as_str() {
"quote" => "quote block".to_owned(), "center" => center_block(context, remaining, input)?,
name @ _ => format!("special block {}", name), "quote" => quote_block(context, remaining, input)?,
_ => special_block(name)(context, remaining, input)?,
}; };
if in_section(context, context_name.as_str()) { Ok((remaining, element))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn center_block<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
original_input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Element<'s>> {
let (remaining, (source, children)) =
greater_block_body(context, input, original_input, "center", "center block")?;
Ok((
remaining,
Element::CenterBlock(CenterBlock { source, children }),
))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn quote_block<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
original_input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Element<'s>> {
let (remaining, (source, children)) =
greater_block_body(context, input, original_input, "quote", "quote block")?;
Ok((
remaining,
Element::QuoteBlock(QuoteBlock { source, children }),
))
}
fn special_block<'s>(
name: &'s str,
) -> impl for<'b, 'g, 'r> Fn(
RefContext<'b, 'g, 'r, 's>,
OrgSource<'s>,
OrgSource<'s>,
) -> Res<OrgSource<'s>, Element<'s>>
+ 's {
let context_name = format!("special block {}", name);
move |context, input, original_input| {
_special_block(context, input, original_input, name, context_name.as_str())
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _special_block<'c, 'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
original_input: OrgSource<'s>,
name: &'s str,
context_name: &'c str,
) -> Res<OrgSource<'s>, Element<'s>> {
let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?;
let (remaining, (source, children)) =
greater_block_body(context, remaining, original_input, name, context_name)?;
Ok((
remaining,
Element::SpecialBlock(SpecialBlock {
source,
children,
name,
parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)),
}),
))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn greater_block_body<'c, 'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
original_input: OrgSource<'s>,
name: &'c str,
context_name: &'c str,
) -> Res<OrgSource<'s>, (&'s str, Vec<Element<'s>>)> {
if in_section(context, context_name) {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same element".into(), "Cannot nest objects of the same element".into(),
)))); ))));
} }
let exit_with_name = greater_block_end(name.into()); let exit_with_name = greater_block_end(name);
let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?; let (remaining, _nl) = tuple((space0, line_ending))(input)?;
let (remaining, _nl) = line_ending(remaining)?;
let contexts = [ let contexts = [
ContextElement::ConsumeTrailingWhitespace(true), ContextElement::ConsumeTrailingWhitespace(true),
ContextElement::Context(context_name.as_str()), ContextElement::Context(context_name),
ContextElement::ExitMatcherNode(ExitMatcherNode { ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Alpha, class: ExitClass::Alpha,
exit_matcher: &exit_with_name, exit_matcher: &exit_with_name,
@@ -76,11 +155,6 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
let parser_context = context.with_additional_node(&contexts[0]); let parser_context = context.with_additional_node(&contexts[0]);
let parser_context = parser_context.with_additional_node(&contexts[1]); let parser_context = parser_context.with_additional_node(&contexts[1]);
let parser_context = parser_context.with_additional_node(&contexts[2]); let parser_context = parser_context.with_additional_node(&contexts[2]);
let parameters = match parameters {
Some((_ws, parameters)) => Some(parameters),
None => None,
};
let element_matcher = parser_with_context!(element(true))(&parser_context); let element_matcher = parser_with_context!(element(true))(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
not(exit_matcher)(remaining)?; not(exit_matcher)(remaining)?;
@@ -104,16 +178,8 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
// Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block // Not checking if parent exit matcher is causing exit because the greater_block_end matcher asserts we matched a full greater block
let source = get_consumed(input, remaining); let source = get_consumed(original_input, remaining);
Ok(( Ok((remaining, (Into::<&str>::into(source), children)))
remaining,
GreaterBlock {
source: source.into(),
name: name.into(),
parameters: parameters.map(|val| Into::<&str>::into(val)),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@@ -123,7 +189,7 @@ fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { fn parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not("\r\n")(input) recognize(many_till(anychar, peek(tuple((space0, line_ending)))))(input)
} }
fn greater_block_end<'c>(name: &'c str) -> impl ContextMatcher + 'c { fn greater_block_end<'c>(name: &'c str) -> impl ContextMatcher + 'c {

View File

@@ -34,6 +34,7 @@ use crate::error::Res;
use crate::parser::object_parser::standard_set_object; use crate::parser::object_parser::standard_set_object;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
use crate::types::DocumentElement; use crate::types::DocumentElement;
use crate::types::Element;
use crate::types::Heading; use crate::types::Heading;
use crate::types::HeadlineLevel; use crate::types::HeadlineLevel;
use crate::types::Object; use crate::types::Object;
@@ -55,6 +56,9 @@ fn _heading<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
parent_star_count: HeadlineLevel, parent_star_count: HeadlineLevel,
) -> Res<OrgSource<'s>, Heading<'s>> { ) -> Res<OrgSource<'s>, Heading<'s>> {
let mut scheduled = None;
let mut deadline = None;
let mut closed = None;
not(|i| context.check_exit_matcher(i))(input)?; not(|i| context.check_exit_matcher(i))(input)?;
let (remaining, pre_headline) = headline(context, input, parent_star_count)?; let (remaining, pre_headline) = headline(context, input, parent_star_count)?;
let section_matcher = parser_with_context!(section)(context); let section_matcher = parser_with_context!(section)(context);
@@ -65,6 +69,14 @@ fn _heading<'b, 'g, 'r, 's>(
let (remaining, mut children) = let (remaining, mut children) =
many0(map(heading_matcher, DocumentElement::Heading))(remaining)?; many0(map(heading_matcher, DocumentElement::Heading))(remaining)?;
if let Some(section) = maybe_section { if let Some(section) = maybe_section {
// If the section has a planning then the timestamp values are copied to the heading.
if let DocumentElement::Section(inner_section) = &section {
if let Some(Element::Planning(planning)) = inner_section.children.first() {
scheduled = planning.scheduled.clone();
deadline = planning.deadline.clone();
closed = planning.closed.clone();
}
}
children.insert(0, section); children.insert(0, section);
} }
let remaining = if children.is_empty() { let remaining = if children.is_empty() {
@@ -94,6 +106,9 @@ fn _heading<'b, 'g, 'r, 's>(
is_comment: pre_headline.comment.is_some(), is_comment: pre_headline.comment.is_some(),
is_archived, is_archived,
is_footnote_section: pre_headline.is_footnote_section, is_footnote_section: pre_headline.is_footnote_section,
scheduled,
deadline,
closed,
}, },
)) ))
} }
@@ -107,6 +122,7 @@ pub(crate) fn detect_headline<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()
/// Fields from a not-yet-fully-parsed Headline. /// Fields from a not-yet-fully-parsed Headline.
/// ///
/// This struct exists to give names to the fields of a partially-parsed Headline to avoid returning a large tuple of nameless fields. /// This struct exists to give names to the fields of a partially-parsed Headline to avoid returning a large tuple of nameless fields.
#[derive(Debug)]
struct PreHeadline<'s> { struct PreHeadline<'s> {
headline_level: HeadlineLevel, headline_level: HeadlineLevel,
star_count: HeadlineLevel, star_count: HeadlineLevel,

View File

@@ -11,8 +11,8 @@ use super::OrgSource;
use crate::context::HeadlineLevelFilter; use crate::context::HeadlineLevelFilter;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::Res; use crate::error::Res;
use crate::iter::AstNode;
use crate::settings::GlobalSettings; use crate::settings::GlobalSettings;
use crate::types::AstNode;
use crate::types::Document; use crate::types::Document;
use crate::types::Keyword; use crate::types::Keyword;

View File

@@ -388,6 +388,7 @@ impl<'s> From<CustomError<OrgSource<'s>>> for CustomError<&'s str> {
CustomError::MyError(err) => CustomError::MyError(err.into()), CustomError::MyError(err) => CustomError::MyError(err.into()),
CustomError::Nom(input, error_kind) => CustomError::Nom(input.into(), error_kind), CustomError::Nom(input, error_kind) => CustomError::Nom(input.into(), error_kind),
CustomError::IO(err) => CustomError::IO(err), CustomError::IO(err) => CustomError::IO(err),
CustomError::BoxedError(err) => CustomError::BoxedError(err),
} }
} }
} }

View File

@@ -3,6 +3,7 @@ use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case; use nom::bytes::complete::tag_no_case;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1; use nom::character::complete::space1;
use nom::combinator::map;
use nom::multi::many1; use nom::multi::many1;
use nom::sequence::tuple; use nom::sequence::tuple;
@@ -16,6 +17,7 @@ use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::start_of_line; use crate::parser::util::start_of_line;
use crate::types::Planning; use crate::types::Planning;
use crate::types::Timestamp;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn planning<'b, 'g, 'r, 's>( pub(crate) fn planning<'b, 'g, 'r, 's>(
@@ -24,7 +26,7 @@ pub(crate) fn planning<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, Planning<'s>> { ) -> Res<OrgSource<'s>, Planning<'s>> {
start_of_line(input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, _planning_parameters) = let (remaining, planning_parameters) =
many1(parser_with_context!(planning_parameter)(context))(remaining)?; many1(parser_with_context!(planning_parameter)(context))(remaining)?;
let (remaining, _trailing_ws) = tuple((space0, org_line_ending))(remaining)?; let (remaining, _trailing_ws) = tuple((space0, org_line_ending))(remaining)?;
@@ -32,26 +34,55 @@ pub(crate) fn planning<'b, 'g, 'r, 's>(
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
let mut scheduled = None;
let mut deadline = None;
let mut closed = None;
for (timestamp_type, timestamp) in planning_parameters.into_iter() {
match timestamp_type {
PlanningTimestampType::Scheduled => {
scheduled = Some(timestamp);
}
PlanningTimestampType::Deadline => {
deadline = Some(timestamp);
}
PlanningTimestampType::Closed => {
closed = Some(timestamp);
}
}
}
Ok(( Ok((
remaining, remaining,
Planning { Planning {
source: source.into(), source: source.into(),
scheduled,
deadline,
closed,
}, },
)) ))
} }
#[derive(Debug)]
enum PlanningTimestampType {
Scheduled,
Deadline,
Closed,
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn planning_parameter<'b, 'g, 'r, 's>( fn planning_parameter<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, (PlanningTimestampType, Timestamp<'s>)> {
let (remaining, _planning_type) = alt(( let (remaining, planning_type) = alt((
tag_no_case("DEADLINE"), map(tag_no_case("DEADLINE"), |_| PlanningTimestampType::Deadline),
tag_no_case("SCHEDULED"), map(tag_no_case("SCHEDULED"), |_| {
tag_no_case("CLOSED"), PlanningTimestampType::Scheduled
}),
map(tag_no_case("CLOSED"), |_| PlanningTimestampType::Closed),
))(input)?; ))(input)?;
let (remaining, _gap) = tuple((tag(":"), space1))(remaining)?; let (remaining, _gap) = tuple((tag(":"), space1))(remaining)?;
let (remaining, _timestamp) = timestamp(context, remaining)?; let (remaining, timestamp) = timestamp(context, remaining)?;
let source = get_consumed(input, remaining); Ok((remaining, (planning_type, timestamp)))
Ok((remaining, source))
} }

View File

@@ -101,7 +101,7 @@ fn node_property<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, NodeProperty<'s>> { ) -> Res<OrgSource<'s>, NodeProperty<'s>> {
let (remaining, (_start_of_line, _leading_whitespace, _open_colon, _name, _close_colon)) = let (remaining, (_start_of_line, _leading_whitespace, _open_colon, name, _close_colon)) =
tuple(( tuple((
start_of_line, start_of_line,
space0, space0,
@@ -120,6 +120,7 @@ fn node_property<'b, 'g, 'r, 's>(
remaining, remaining,
NodeProperty { NodeProperty {
source: source.into(), source: source.into(),
name: Into::<&str>::into(name),
value: None, value: None,
}, },
)) ))
@@ -132,6 +133,7 @@ fn node_property<'b, 'g, 'r, 's>(
remaining, remaining,
NodeProperty { NodeProperty {
source: source.into(), source: source.into(),
name: Into::<&str>::into(name),
value: Some(value.into()), value: Some(value.into()),
}, },
)) ))

View File

@@ -3,8 +3,8 @@ use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::combinator::eof;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
@@ -17,6 +17,7 @@ use super::keyword::table_formula_keyword;
use super::object_parser::table_cell_set_object; use super::object_parser::table_cell_set_object;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::util::exit_matcher_parser; use super::util::exit_matcher_parser;
use super::util::org_line_ending;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::ExitClass; use crate::context::ExitClass;
@@ -105,7 +106,7 @@ fn org_mode_table_row_rule<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, TableRow<'s>> { ) -> Res<OrgSource<'s>, TableRow<'s>> {
start_of_line(input)?; start_of_line(input)?;
let (remaining, _) = tuple((space0, tag("|-"), is_not("\r\n"), line_ending))(input)?; let (remaining, _) = tuple((space0, tag("|-"), opt(is_not("\r\n")), org_line_ending))(input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, remaining,
@@ -125,7 +126,7 @@ fn org_mode_table_row_regular<'b, 'g, 'r, 's>(
let (remaining, _) = tuple((space0, tag("|")))(input)?; let (remaining, _) = tuple((space0, tag("|")))(input)?;
let (remaining, children) = let (remaining, children) =
many1(parser_with_context!(org_mode_table_cell)(context))(remaining)?; many1(parser_with_context!(org_mode_table_cell)(context))(remaining)?;
let (remaining, _tail) = recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?; let (remaining, _tail) = recognize(tuple((space0, org_line_ending)))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, remaining,

View File

@@ -1,10 +1,10 @@
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::character::complete::anychar; use nom::character::complete::anychar;
use nom::character::complete::digit0;
use nom::character::complete::digit1; use nom::character::complete::digit1;
use nom::character::complete::one_of; use nom::character::complete::one_of;
use nom::character::complete::space1; use nom::character::complete::space1;
use nom::combinator::map;
use nom::combinator::opt; use nom::combinator::opt;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
@@ -21,7 +21,21 @@ use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::types::Date;
use crate::types::DayOfMonth;
use crate::types::Hour;
use crate::types::Minute;
use crate::types::Month;
use crate::types::Repeater;
use crate::types::RepeaterType;
use crate::types::Time;
use crate::types::TimeUnit;
use crate::types::Timestamp; use crate::types::Timestamp;
use crate::types::TimestampRangeType;
use crate::types::TimestampType;
use crate::types::WarningDelay;
use crate::types::WarningDelayType;
use crate::types::Year;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn timestamp<'b, 'g, 'r, 's>( pub(crate) fn timestamp<'b, 'g, 'r, 's>(
@@ -57,6 +71,14 @@ fn diary_timestamp<'b, 'g, 'r, 's>(
remaining, remaining,
Timestamp { Timestamp {
source: source.into(), source: source.into(),
timestamp_type: TimestampType::Diary,
range_type: TimestampRangeType::None,
start: None,
end: None,
start_time: None,
end_time: None,
repeater: None,
warning_delay: None,
}, },
)) ))
} }
@@ -97,17 +119,26 @@ fn active_timestamp<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _) = tag("<")(input)?; let (remaining, _) = tag("<")(input)?;
let (remaining, _date) = date(context, remaining)?; let (remaining, start) = date(context, remaining)?;
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode { let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma, class: ExitClass::Gamma,
exit_matcher: &active_time_rest_end, exit_matcher: &active_time_rest_end,
}); });
let time_context = context.with_additional_node(&time_context); let time_context = context.with_additional_node(&time_context);
let (remaining, _time) = let (remaining, time) = opt(tuple((
opt(tuple((space1, parser_with_context!(time)(&time_context))))(remaining)?; space1,
let (remaining, _repeater) = parser_with_context!(time(true))(&time_context),
)))(remaining)?;
let remaining = if time.is_none() {
// Upstream org-mode accepts malformed timestamps. For example '<2016-02-14 Sun ++y>'.
let (remain, _) = opt(parser_with_context!(time_rest)(&time_context))(remaining)?;
remain
} else {
remaining
};
let (remaining, repeater) =
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?; opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
let (remaining, _warning_delay) = opt(tuple(( let (remaining, warning_delay) = opt(tuple((
space1, space1,
parser_with_context!(warning_delay)(context), parser_with_context!(warning_delay)(context),
)))(remaining)?; )))(remaining)?;
@@ -121,6 +152,14 @@ fn active_timestamp<'b, 'g, 'r, 's>(
remaining, remaining,
Timestamp { Timestamp {
source: source.into(), source: source.into(),
timestamp_type: TimestampType::Active,
range_type: TimestampRangeType::None,
start: Some(start.clone()),
end: Some(start),
start_time: time.as_ref().map(|(_, time)| time.clone()),
end_time: time.map(|(_, time)| time),
repeater: repeater.map(|(_, repeater)| repeater),
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
}, },
)) ))
} }
@@ -131,17 +170,26 @@ fn inactive_timestamp<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _) = tag("[")(input)?; let (remaining, _) = tag("[")(input)?;
let (remaining, _date) = date(context, remaining)?; let (remaining, start) = date(context, remaining)?;
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode { let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma, class: ExitClass::Gamma,
exit_matcher: &inactive_time_rest_end, exit_matcher: &inactive_time_rest_end,
}); });
let time_context = context.with_additional_node(&time_context); let time_context = context.with_additional_node(&time_context);
let (remaining, _time) = let (remaining, time) = opt(tuple((
opt(tuple((space1, parser_with_context!(time)(&time_context))))(remaining)?; space1,
let (remaining, _repeater) = parser_with_context!(time(true))(&time_context),
)))(remaining)?;
let remaining = if time.is_none() {
// Upstream org-mode accepts malformed timestamps. For example '<2016-02-14 Sun ++y>'.
let (remain, _) = opt(parser_with_context!(time_rest)(&time_context))(remaining)?;
remain
} else {
remaining
};
let (remaining, repeater) =
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?; opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
let (remaining, _warning_delay) = opt(tuple(( let (remaining, warning_delay) = opt(tuple((
space1, space1,
parser_with_context!(warning_delay)(context), parser_with_context!(warning_delay)(context),
)))(remaining)?; )))(remaining)?;
@@ -155,6 +203,14 @@ fn inactive_timestamp<'b, 'g, 'r, 's>(
remaining, remaining,
Timestamp { Timestamp {
source: source.into(), source: source.into(),
timestamp_type: TimestampType::Inactive,
range_type: TimestampRangeType::None,
start: Some(start.clone()),
end: Some(start),
start_time: time.as_ref().map(|(_, time)| time.clone()),
end_time: time.map(|(_, time)| time),
repeater: repeater.map(|(_, repeater)| repeater),
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
}, },
)) ))
} }
@@ -164,10 +220,10 @@ fn active_date_range_timestamp<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _first_timestamp) = active_timestamp(context, input)?; let (remaining, first_timestamp) = active_timestamp(context, input)?;
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace // TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
let (remaining, _separator) = tag("--")(remaining)?; let (remaining, _separator) = tag("--")(remaining)?;
let (remaining, _second_timestamp) = active_timestamp(context, remaining)?; let (remaining, second_timestamp) = active_timestamp(context, remaining)?;
let (remaining, _trailing_whitespace) = let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
@@ -177,6 +233,16 @@ fn active_date_range_timestamp<'b, 'g, 'r, 's>(
remaining, remaining,
Timestamp { Timestamp {
source: source.into(), source: source.into(),
timestamp_type: TimestampType::ActiveRange,
range_type: TimestampRangeType::DateRange,
start: first_timestamp.start,
end: second_timestamp.end,
start_time: first_timestamp.start_time,
end_time: second_timestamp.end_time,
repeater: first_timestamp.repeater.or(second_timestamp.repeater),
warning_delay: first_timestamp
.warning_delay
.or(second_timestamp.warning_delay),
}, },
)) ))
} }
@@ -187,7 +253,7 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _) = tag("<")(input)?; let (remaining, _) = tag("<")(input)?;
let (remaining, _date) = date(context, remaining)?; let (remaining, start_date) = date(context, remaining)?;
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode { let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma, class: ExitClass::Gamma,
exit_matcher: &active_time_rest_end, exit_matcher: &active_time_rest_end,
@@ -198,13 +264,15 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>(
exit_matcher: &time_range_rest_end, exit_matcher: &time_range_rest_end,
}); });
let first_time_context = time_context.with_additional_node(&first_time_context); let first_time_context = time_context.with_additional_node(&first_time_context);
let (remaining, _first_time) = let (remaining, (_, first_time)) = tuple((
tuple((space1, parser_with_context!(time)(&first_time_context)))(remaining)?; space1,
parser_with_context!(time(false))(&first_time_context),
))(remaining)?;
let (remaining, _) = tag("-")(remaining)?; let (remaining, _) = tag("-")(remaining)?;
let (remaining, _second_time) = parser_with_context!(time)(&time_context)(remaining)?; let (remaining, second_time) = parser_with_context!(time(true))(&time_context)(remaining)?;
let (remaining, _repeater) = let (remaining, repeater) =
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?; opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
let (remaining, _warning_delay) = opt(tuple(( let (remaining, warning_delay) = opt(tuple((
space1, space1,
parser_with_context!(warning_delay)(context), parser_with_context!(warning_delay)(context),
)))(remaining)?; )))(remaining)?;
@@ -218,6 +286,14 @@ fn active_time_range_timestamp<'b, 'g, 'r, 's>(
remaining, remaining,
Timestamp { Timestamp {
source: source.into(), source: source.into(),
timestamp_type: TimestampType::ActiveRange,
range_type: TimestampRangeType::TimeRange,
start: Some(start_date.clone()),
end: Some(start_date),
start_time: Some(first_time),
end_time: Some(second_time),
repeater: repeater.map(|(_, repeater)| repeater),
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
}, },
)) ))
} }
@@ -227,10 +303,10 @@ fn inactive_date_range_timestamp<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _first_timestamp) = inactive_timestamp(context, input)?; let (remaining, first_timestamp) = inactive_timestamp(context, input)?;
// TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace // TODO: Does the space0 at the end of the active/inactive timestamp parsers cause this to be incorrect? I could use a look-behind to make sure the preceding character is not whitespace
let (remaining, _separator) = tag("--")(remaining)?; let (remaining, _separator) = tag("--")(remaining)?;
let (remaining, _second_timestamp) = inactive_timestamp(context, remaining)?; let (remaining, second_timestamp) = inactive_timestamp(context, remaining)?;
let (remaining, _trailing_whitespace) = let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
@@ -240,6 +316,17 @@ fn inactive_date_range_timestamp<'b, 'g, 'r, 's>(
remaining, remaining,
Timestamp { Timestamp {
source: source.into(), source: source.into(),
timestamp_type: TimestampType::InactiveRange,
range_type: TimestampRangeType::DateRange,
start: first_timestamp.start,
end: second_timestamp.end,
start_time: first_timestamp.start_time,
end_time: second_timestamp.end_time,
repeater: first_timestamp.repeater.or(second_timestamp.repeater),
warning_delay: first_timestamp
.warning_delay
.or(second_timestamp.warning_delay),
}, },
)) ))
} }
@@ -250,7 +337,7 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Timestamp<'s>> { ) -> Res<OrgSource<'s>, Timestamp<'s>> {
let (remaining, _) = tag("[")(input)?; let (remaining, _) = tag("[")(input)?;
let (remaining, _date) = date(context, remaining)?; let (remaining, start_date) = date(context, remaining)?;
let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode { let time_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma, class: ExitClass::Gamma,
exit_matcher: &inactive_time_rest_end, exit_matcher: &inactive_time_rest_end,
@@ -261,13 +348,15 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
exit_matcher: &time_range_rest_end, exit_matcher: &time_range_rest_end,
}); });
let first_time_context = time_context.with_additional_node(&first_time_context); let first_time_context = time_context.with_additional_node(&first_time_context);
let (remaining, _first_time) = let (remaining, (_, first_time)) = tuple((
tuple((space1, parser_with_context!(time)(&first_time_context)))(remaining)?; space1,
parser_with_context!(time(false))(&first_time_context),
))(remaining)?;
let (remaining, _) = tag("-")(remaining)?; let (remaining, _) = tag("-")(remaining)?;
let (remaining, _second_time) = parser_with_context!(time)(&time_context)(remaining)?; let (remaining, second_time) = parser_with_context!(time(true))(&time_context)(remaining)?;
let (remaining, _repeater) = let (remaining, repeater) =
opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?; opt(tuple((space1, parser_with_context!(repeater)(context))))(remaining)?;
let (remaining, _warning_delay) = opt(tuple(( let (remaining, warning_delay) = opt(tuple((
space1, space1,
parser_with_context!(warning_delay)(context), parser_with_context!(warning_delay)(context),
)))(remaining)?; )))(remaining)?;
@@ -281,6 +370,14 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
remaining, remaining,
Timestamp { Timestamp {
source: source.into(), source: source.into(),
timestamp_type: TimestampType::InactiveRange,
range_type: TimestampRangeType::TimeRange,
start: Some(start_date.clone()),
end: Some(start_date),
start_time: Some(first_time),
end_time: Some(second_time),
repeater: repeater.map(|(_, repeater)| repeater),
warning_delay: warning_delay.map(|(_, warning_delay)| warning_delay),
}, },
)) ))
} }
@@ -289,18 +386,33 @@ fn inactive_time_range_timestamp<'b, 'g, 'r, 's>(
fn date<'b, 'g, 'r, 's>( fn date<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, Date<'s>> {
let (remaining, _year) = verify(digit1, |year: &OrgSource<'_>| year.len() == 4)(input)?; let (remaining, year) = verify(digit1, |year: &OrgSource<'_>| year.len() == 4)(input)?;
let (remaining, _) = tag("-")(remaining)?; let (remaining, _) = tag("-")(remaining)?;
let (remaining, _month) = verify(digit1, |month: &OrgSource<'_>| month.len() == 2)(remaining)?; let (remaining, month) = verify(digit1, |month: &OrgSource<'_>| month.len() == 2)(remaining)?;
let (remaining, _) = tag("-")(remaining)?; let (remaining, _) = tag("-")(remaining)?;
let (remaining, _day_of_month) = verify(digit1, |day_of_month: &OrgSource<'_>| { let (remaining, day_of_month) = verify(digit1, |day_of_month: &OrgSource<'_>| {
day_of_month.len() == 2 day_of_month.len() == 2
})(remaining)?; })(remaining)?;
let (remaining, _dayname) = let (remaining, day_name) =
opt(tuple((space1, parser_with_context!(dayname)(context))))(remaining)?; opt(tuple((space1, parser_with_context!(dayname)(context))))(remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, source)) let year = Year::new(Into::<&str>::into(year))
.expect("TODO: I should be able to return CustomError from nom parsers.");
let month = Month::new(Into::<&str>::into(month))
.expect("TODO: I should be able to return CustomError from nom parsers.");
let day_of_month = DayOfMonth::new(Into::<&str>::into(day_of_month))
.expect("TODO: I should be able to return CustomError from nom parsers.");
let date = Date::new(
year,
month,
day_of_month,
day_name.map(|(_, day_name)| Into::<&str>::into(day_name)),
)
.expect("TODO: I should be able to return CustomError from nom parsers.");
Ok((remaining, date))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@@ -335,20 +447,39 @@ fn dayname_end<'b, 'g, 'r, 's>(
}))(input) }))(input)
} }
const fn time<'c>(
allow_rest: bool,
) -> impl for<'b, 'g, 'r, 's> Fn(RefContext<'b, 'g, 'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, Time<'s>>
{
move |context, input| _time(context, input, allow_rest)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn time<'b, 'g, 'r, 's>( fn _time<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { allow_rest: bool,
let (remaining, _hour) = verify(digit1, |hour: &OrgSource<'_>| { ) -> Res<OrgSource<'s>, Time<'s>> {
let (remaining, hour) = verify(digit1, |hour: &OrgSource<'_>| {
hour.len() >= 1 && hour.len() <= 2 hour.len() >= 1 && hour.len() <= 2
})(input)?; })(input)?;
let (remaining, _) = tag(":")(remaining)?; let (remaining, _) = tag(":")(remaining)?;
let (remaining, _minute) = let (remaining, minute) =
verify(digit1, |minute: &OrgSource<'_>| minute.len() == 2)(remaining)?; verify(digit1, |minute: &OrgSource<'_>| minute.len() == 2)(remaining)?;
let (remaining, _time_rest) = opt(parser_with_context!(time_rest)(context))(remaining)?; let (remaining, time_rest) = if allow_rest {
let source = get_consumed(input, remaining); opt(parser_with_context!(time_rest)(context))(remaining)?
Ok((remaining, source)) } else {
(remaining, None)
};
let hour = Hour::new(Into::<&str>::into(hour))
.expect("TODO: I should be able to return CustomError from nom parsers.");
let minute = Minute::new(Into::<&str>::into(minute))
.expect("TODO: I should be able to return CustomError from nom parsers.");
let time = Time::new(hour, minute, time_rest.map(Into::<&str>::into))
.expect("TODO: I should be able to return CustomError from nom parsers.");
Ok((remaining, time))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@@ -401,8 +532,10 @@ fn time_range_rest_end<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
// We pop off the most recent context element to get a context tree with just the active/inactive_time_rest_end exit matcher (removing this function from the exit matcher chain) because the 2nd time in the range does not end when a "-TIME" pattern is found. // We pop off the most recent context element to get a context tree with just the active/inactive_time_rest_end exit matcher (removing this function from the exit matcher chain) because the 2nd time in the range does not end when a "-TIME" pattern is found.
let parent_node = context.get_parent().expect("Two context elements are added to the tree when adding this exit matcher, so it should be impossible for this to return None."); let parent_node = context.get_parent().expect("Two context elements are added to the tree when adding this exit matcher, so it should be impossible for this to return None.");
let exit_contents = let exit_contents = recognize(tuple((
recognize(tuple((tag("-"), parser_with_context!(time)(&parent_node))))(input); tag("-"),
parser_with_context!(time(true))(&parent_node),
)))(input);
exit_contents exit_contents
} }
@@ -410,29 +543,66 @@ fn time_range_rest_end<'b, 'g, 'r, 's>(
fn repeater<'b, 'g, 'r, 's>( fn repeater<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>, _context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, Repeater> {
// + for cumulative type // + for cumulative type
// ++ for catch-up type // ++ for catch-up type
// .+ for restart type // .+ for restart type
let (remaining, _mark) = alt((tag("++"), tag("+"), tag(".+")))(input)?; let (remaining, repeater_type) = alt((
let (remaining, _value) = digit0(remaining)?; map(tag("++"), |_| RepeaterType::CatchUp),
map(tag("+"), |_| RepeaterType::Cumulative),
map(tag(".+"), |_| RepeaterType::Restart),
))(input)?;
let (remaining, value) = digit1(remaining)?;
let value = Into::<&str>::into(value)
.parse()
.expect("digit1 ensures this will parse as a number.");
// h = hour, d = day, w = week, m = month, y = year // h = hour, d = day, w = week, m = month, y = year
let (remaining, _unit) = recognize(one_of("hdwmy"))(remaining)?; let (remaining, unit) = alt((
let source = get_consumed(input, remaining); map(tag("h"), |_| TimeUnit::Hour),
Ok((remaining, source)) map(tag("d"), |_| TimeUnit::Day),
map(tag("w"), |_| TimeUnit::Week),
map(tag("m"), |_| TimeUnit::Month),
map(tag("y"), |_| TimeUnit::Year),
))(remaining)?;
Ok((
remaining,
Repeater {
repeater_type,
value,
unit,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn warning_delay<'b, 'g, 'r, 's>( fn warning_delay<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>, _context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, WarningDelay> {
// - for all type // - for all type
// -- for first type // -- for first type
let (remaining, _mark) = alt((tag("--"), tag("-")))(input)?; let (remaining, warning_delay_type) = alt((
let (remaining, _value) = digit0(remaining)?; map(tag("--"), |_| WarningDelayType::First),
map(tag("-"), |_| WarningDelayType::All),
))(input)?;
let (remaining, value) = digit1(remaining)?;
let value = Into::<&str>::into(value)
.parse()
.expect("digit1 ensures this will parse as a number.");
// h = hour, d = day, w = week, m = month, y = year // h = hour, d = day, w = week, m = month, y = year
let (remaining, _unit) = recognize(one_of("hdwmy"))(remaining)?; let (remaining, unit) = alt((
let source = get_consumed(input, remaining); map(tag("h"), |_| TimeUnit::Hour),
Ok((remaining, source)) map(tag("d"), |_| TimeUnit::Day),
map(tag("w"), |_| TimeUnit::Week),
map(tag("m"), |_| TimeUnit::Month),
map(tag("y"), |_| TimeUnit::Year),
))(remaining)?;
Ok((
remaining,
WarningDelay {
warning_delay_type,
value,
unit,
},
))
} }

View File

@@ -1,4 +1,7 @@
use super::macros::to_ast_node; use super::macros::to_ast_node;
use super::CenterBlock;
use super::QuoteBlock;
use super::SpecialBlock;
use crate::types::AngleLink; use crate::types::AngleLink;
use crate::types::BabelCall; use crate::types::BabelCall;
use crate::types::Bold; use crate::types::Bold;
@@ -21,7 +24,7 @@ use crate::types::ExportSnippet;
use crate::types::FixedWidthArea; use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition; use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference; use crate::types::FootnoteReference;
use crate::types::GreaterBlock; use crate::types::GetStandardProperties;
use crate::types::Heading; use crate::types::Heading;
use crate::types::HorizontalRule; use crate::types::HorizontalRule;
use crate::types::InlineBabelCall; use crate::types::InlineBabelCall;
@@ -68,7 +71,9 @@ pub enum AstNode<'r, 's> {
Paragraph(&'r Paragraph<'s>), Paragraph(&'r Paragraph<'s>),
PlainList(&'r PlainList<'s>), PlainList(&'r PlainList<'s>),
PlainListItem(&'r PlainListItem<'s>), PlainListItem(&'r PlainListItem<'s>),
GreaterBlock(&'r GreaterBlock<'s>), CenterBlock(&'r CenterBlock<'s>),
QuoteBlock(&'r QuoteBlock<'s>),
SpecialBlock(&'r SpecialBlock<'s>),
DynamicBlock(&'r DynamicBlock<'s>), DynamicBlock(&'r DynamicBlock<'s>),
FootnoteDefinition(&'r FootnoteDefinition<'s>), FootnoteDefinition(&'r FootnoteDefinition<'s>),
Comment(&'r Comment<'s>), Comment(&'r Comment<'s>),
@@ -135,7 +140,9 @@ impl<'r, 's> From<&'r Element<'s>> for AstNode<'r, 's> {
match value { match value {
Element::Paragraph(inner) => inner.into(), Element::Paragraph(inner) => inner.into(),
Element::PlainList(inner) => inner.into(), Element::PlainList(inner) => inner.into(),
Element::GreaterBlock(inner) => inner.into(), Element::CenterBlock(inner) => inner.into(),
Element::QuoteBlock(inner) => inner.into(),
Element::SpecialBlock(inner) => inner.into(),
Element::DynamicBlock(inner) => inner.into(), Element::DynamicBlock(inner) => inner.into(),
Element::FootnoteDefinition(inner) => inner.into(), Element::FootnoteDefinition(inner) => inner.into(),
Element::Comment(inner) => inner.into(), Element::Comment(inner) => inner.into(),
@@ -199,7 +206,9 @@ to_ast_node!(&'r Section<'s>, AstNode::Section);
to_ast_node!(&'r Paragraph<'s>, AstNode::Paragraph); to_ast_node!(&'r Paragraph<'s>, AstNode::Paragraph);
to_ast_node!(&'r PlainList<'s>, AstNode::PlainList); to_ast_node!(&'r PlainList<'s>, AstNode::PlainList);
to_ast_node!(&'r PlainListItem<'s>, AstNode::PlainListItem); to_ast_node!(&'r PlainListItem<'s>, AstNode::PlainListItem);
to_ast_node!(&'r GreaterBlock<'s>, AstNode::GreaterBlock); to_ast_node!(&'r CenterBlock<'s>, AstNode::CenterBlock);
to_ast_node!(&'r QuoteBlock<'s>, AstNode::QuoteBlock);
to_ast_node!(&'r SpecialBlock<'s>, AstNode::SpecialBlock);
to_ast_node!(&'r DynamicBlock<'s>, AstNode::DynamicBlock); to_ast_node!(&'r DynamicBlock<'s>, AstNode::DynamicBlock);
to_ast_node!(&'r FootnoteDefinition<'s>, AstNode::FootnoteDefinition); to_ast_node!(&'r FootnoteDefinition<'s>, AstNode::FootnoteDefinition);
to_ast_node!(&'r Comment<'s>, AstNode::Comment); to_ast_node!(&'r Comment<'s>, AstNode::Comment);
@@ -249,3 +258,68 @@ to_ast_node!(&'r Subscript<'s>, AstNode::Subscript);
to_ast_node!(&'r Superscript<'s>, AstNode::Superscript); to_ast_node!(&'r Superscript<'s>, AstNode::Superscript);
to_ast_node!(&'r TableCell<'s>, AstNode::TableCell); to_ast_node!(&'r TableCell<'s>, AstNode::TableCell);
to_ast_node!(&'r Timestamp<'s>, AstNode::Timestamp); to_ast_node!(&'r Timestamp<'s>, AstNode::Timestamp);
impl<'r, 's> GetStandardProperties<'s> for AstNode<'r, 's> {
fn get_standard_properties<'b>(&'b self) -> &'b dyn crate::types::StandardProperties<'s> {
match self {
AstNode::Document(inner) => *inner,
AstNode::Heading(inner) => *inner,
AstNode::Section(inner) => *inner,
AstNode::Paragraph(inner) => *inner,
AstNode::PlainList(inner) => *inner,
AstNode::PlainListItem(inner) => *inner,
AstNode::CenterBlock(inner) => *inner,
AstNode::QuoteBlock(inner) => *inner,
AstNode::SpecialBlock(inner) => *inner,
AstNode::DynamicBlock(inner) => *inner,
AstNode::FootnoteDefinition(inner) => *inner,
AstNode::Comment(inner) => *inner,
AstNode::Drawer(inner) => *inner,
AstNode::PropertyDrawer(inner) => *inner,
AstNode::NodeProperty(inner) => *inner,
AstNode::Table(inner) => *inner,
AstNode::TableRow(inner) => *inner,
AstNode::VerseBlock(inner) => *inner,
AstNode::CommentBlock(inner) => *inner,
AstNode::ExampleBlock(inner) => *inner,
AstNode::ExportBlock(inner) => *inner,
AstNode::SrcBlock(inner) => *inner,
AstNode::Clock(inner) => *inner,
AstNode::DiarySexp(inner) => *inner,
AstNode::Planning(inner) => *inner,
AstNode::FixedWidthArea(inner) => *inner,
AstNode::HorizontalRule(inner) => *inner,
AstNode::Keyword(inner) => *inner,
AstNode::BabelCall(inner) => *inner,
AstNode::LatexEnvironment(inner) => *inner,
AstNode::Bold(inner) => *inner,
AstNode::Italic(inner) => *inner,
AstNode::Underline(inner) => *inner,
AstNode::StrikeThrough(inner) => *inner,
AstNode::Code(inner) => *inner,
AstNode::Verbatim(inner) => *inner,
AstNode::PlainText(inner) => *inner,
AstNode::RegularLink(inner) => *inner,
AstNode::RadioLink(inner) => *inner,
AstNode::RadioTarget(inner) => *inner,
AstNode::PlainLink(inner) => *inner,
AstNode::AngleLink(inner) => *inner,
AstNode::OrgMacro(inner) => *inner,
AstNode::Entity(inner) => *inner,
AstNode::LatexFragment(inner) => *inner,
AstNode::ExportSnippet(inner) => *inner,
AstNode::FootnoteReference(inner) => *inner,
AstNode::Citation(inner) => *inner,
AstNode::CitationReference(inner) => *inner,
AstNode::InlineBabelCall(inner) => *inner,
AstNode::InlineSourceBlock(inner) => *inner,
AstNode::LineBreak(inner) => *inner,
AstNode::Target(inner) => *inner,
AstNode::StatisticsCookie(inner) => *inner,
AstNode::Subscript(inner) => *inner,
AstNode::Superscript(inner) => *inner,
AstNode::TableCell(inner) => *inner,
AstNode::Timestamp(inner) => *inner,
}
}
}

View File

@@ -4,6 +4,7 @@ use super::Element;
use super::GetStandardProperties; use super::GetStandardProperties;
use super::Object; use super::Object;
use super::StandardProperties; use super::StandardProperties;
use super::Timestamp;
pub type PriorityCookie = u8; pub type PriorityCookie = u8;
pub type HeadlineLevel = u16; pub type HeadlineLevel = u16;
@@ -29,6 +30,9 @@ pub struct Heading<'s> {
pub is_comment: bool, pub is_comment: bool,
pub is_archived: bool, pub is_archived: bool,
pub is_footnote_section: bool, pub is_footnote_section: bool,
pub scheduled: Option<Timestamp<'s>>,
pub deadline: Option<Timestamp<'s>>,
pub closed: Option<Timestamp<'s>>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -50,7 +54,7 @@ pub enum TodoKeywordType {
} }
impl<'s> GetStandardProperties<'s> for DocumentElement<'s> { impl<'s> GetStandardProperties<'s> for DocumentElement<'s> {
fn get_standard_properties(&'s self) -> &'s dyn StandardProperties { fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
match self { match self {
DocumentElement::Heading(inner) => inner, DocumentElement::Heading(inner) => inner,
DocumentElement::Section(inner) => inner, DocumentElement::Section(inner) => inner,
@@ -59,19 +63,19 @@ impl<'s> GetStandardProperties<'s> for DocumentElement<'s> {
} }
impl<'s> StandardProperties<'s> for Document<'s> { impl<'s> StandardProperties<'s> for Document<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Section<'s> { impl<'s> StandardProperties<'s> for Section<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Heading<'s> { impl<'s> StandardProperties<'s> for Heading<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }

View File

@@ -1,6 +1,5 @@
use super::greater_element::DynamicBlock; use super::greater_element::DynamicBlock;
use super::greater_element::FootnoteDefinition; use super::greater_element::FootnoteDefinition;
use super::greater_element::GreaterBlock;
use super::greater_element::PlainList; use super::greater_element::PlainList;
use super::greater_element::PropertyDrawer; use super::greater_element::PropertyDrawer;
use super::greater_element::Table; use super::greater_element::Table;
@@ -19,16 +18,21 @@ use super::lesser_element::Paragraph;
use super::lesser_element::Planning; use super::lesser_element::Planning;
use super::lesser_element::SrcBlock; use super::lesser_element::SrcBlock;
use super::lesser_element::VerseBlock; use super::lesser_element::VerseBlock;
use super::CenterBlock;
use super::Drawer; use super::Drawer;
use super::GetStandardProperties; use super::GetStandardProperties;
use super::QuoteBlock;
use super::SetSource; use super::SetSource;
use super::SpecialBlock;
use super::StandardProperties; use super::StandardProperties;
#[derive(Debug)] #[derive(Debug)]
pub enum Element<'s> { pub enum Element<'s> {
Paragraph(Paragraph<'s>), Paragraph(Paragraph<'s>),
PlainList(PlainList<'s>), PlainList(PlainList<'s>),
GreaterBlock(GreaterBlock<'s>), CenterBlock(CenterBlock<'s>),
QuoteBlock(QuoteBlock<'s>),
SpecialBlock(SpecialBlock<'s>),
DynamicBlock(DynamicBlock<'s>), DynamicBlock(DynamicBlock<'s>),
FootnoteDefinition(FootnoteDefinition<'s>), FootnoteDefinition(FootnoteDefinition<'s>),
Comment(Comment<'s>), Comment(Comment<'s>),
@@ -56,7 +60,9 @@ impl<'s> SetSource<'s> for Element<'s> {
match self { match self {
Element::Paragraph(obj) => obj.source = source, Element::Paragraph(obj) => obj.source = source,
Element::PlainList(obj) => obj.source = source, Element::PlainList(obj) => obj.source = source,
Element::GreaterBlock(obj) => obj.source = source, Element::CenterBlock(obj) => obj.source = source,
Element::QuoteBlock(obj) => obj.source = source,
Element::SpecialBlock(obj) => obj.source = source,
Element::DynamicBlock(obj) => obj.source = source, Element::DynamicBlock(obj) => obj.source = source,
Element::FootnoteDefinition(obj) => obj.source = source, Element::FootnoteDefinition(obj) => obj.source = source,
Element::Comment(obj) => obj.source = source, Element::Comment(obj) => obj.source = source,
@@ -81,11 +87,13 @@ impl<'s> SetSource<'s> for Element<'s> {
} }
impl<'s> GetStandardProperties<'s> for Element<'s> { impl<'s> GetStandardProperties<'s> for Element<'s> {
fn get_standard_properties(&'s self) -> &'s dyn StandardProperties { fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
match self { match self {
Element::Paragraph(inner) => inner, Element::Paragraph(inner) => inner,
Element::PlainList(inner) => inner, Element::PlainList(inner) => inner,
Element::GreaterBlock(inner) => inner, Element::CenterBlock(inner) => inner,
Element::QuoteBlock(inner) => inner,
Element::SpecialBlock(inner) => inner,
Element::DynamicBlock(inner) => inner, Element::DynamicBlock(inner) => inner,
Element::FootnoteDefinition(inner) => inner, Element::FootnoteDefinition(inner) => inner,
Element::Comment(inner) => inner, Element::Comment(inner) => inner,

View File

@@ -2,11 +2,11 @@ use super::StandardProperties;
pub trait GetStandardProperties<'s> { pub trait GetStandardProperties<'s> {
// TODO: Can I eliminate this dynamic dispatch, perhaps using nominal generic structs? Low prioritiy since this is not used during parsing. // TODO: Can I eliminate this dynamic dispatch, perhaps using nominal generic structs? Low prioritiy since this is not used during parsing.
fn get_standard_properties(&'s self) -> &'s dyn StandardProperties; fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s>;
} }
impl<'s, I: StandardProperties<'s>> GetStandardProperties<'s> for I { impl<'s, I: StandardProperties<'s>> GetStandardProperties<'s> for I {
fn get_standard_properties(&'s self) -> &'s dyn StandardProperties { fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
self self
} }
} }

View File

@@ -44,7 +44,19 @@ pub enum CheckboxType {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct GreaterBlock<'s> { pub struct CenterBlock<'s> {
pub source: &'s str,
pub children: Vec<Element<'s>>,
}
#[derive(Debug)]
pub struct QuoteBlock<'s> {
pub source: &'s str,
pub children: Vec<Element<'s>>,
}
#[derive(Debug)]
pub struct SpecialBlock<'s> {
pub source: &'s str, pub source: &'s str,
pub name: &'s str, pub name: &'s str,
pub parameters: Option<&'s str>, pub parameters: Option<&'s str>,
@@ -82,6 +94,7 @@ pub struct PropertyDrawer<'s> {
#[derive(Debug)] #[derive(Debug)]
pub struct NodeProperty<'s> { pub struct NodeProperty<'s> {
pub source: &'s str, pub source: &'s str,
pub name: &'s str,
pub value: Option<&'s str>, pub value: Option<&'s str>,
} }
@@ -98,62 +111,80 @@ pub struct TableRow<'s> {
pub children: Vec<TableCell<'s>>, pub children: Vec<TableCell<'s>>,
} }
#[derive(Debug)]
pub enum TableRowType {
Standard,
Rule,
}
impl<'s> StandardProperties<'s> for PlainList<'s> { impl<'s> StandardProperties<'s> for PlainList<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for PlainListItem<'s> { impl<'s> StandardProperties<'s> for PlainListItem<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for GreaterBlock<'s> { impl<'s> StandardProperties<'s> for CenterBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source
}
}
impl<'s> StandardProperties<'s> for QuoteBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
}
}
impl<'s> StandardProperties<'s> for SpecialBlock<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for DynamicBlock<'s> { impl<'s> StandardProperties<'s> for DynamicBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for FootnoteDefinition<'s> { impl<'s> StandardProperties<'s> for FootnoteDefinition<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Drawer<'s> { impl<'s> StandardProperties<'s> for Drawer<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for PropertyDrawer<'s> { impl<'s> StandardProperties<'s> for PropertyDrawer<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for NodeProperty<'s> { impl<'s> StandardProperties<'s> for NodeProperty<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Table<'s> { impl<'s> StandardProperties<'s> for Table<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for TableRow<'s> { impl<'s> StandardProperties<'s> for TableRow<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
@@ -169,3 +200,13 @@ impl<'s> PlainListItem<'s> {
.map(|(checkbox_type, _)| checkbox_type) .map(|(checkbox_type, _)| checkbox_type)
} }
} }
impl<'s> TableRow<'s> {
pub fn get_type(&self) -> TableRowType {
if self.children.is_empty() {
TableRowType::Rule
} else {
TableRowType::Standard
}
}
}

View File

@@ -1,6 +1,7 @@
use super::object::Object; use super::object::Object;
use super::PlainText; use super::PlainText;
use super::StandardProperties; use super::StandardProperties;
use super::Timestamp;
#[derive(Debug)] #[derive(Debug)]
pub struct Paragraph<'s> { pub struct Paragraph<'s> {
@@ -11,6 +12,7 @@ pub struct Paragraph<'s> {
#[derive(Debug)] #[derive(Debug)]
pub struct Comment<'s> { pub struct Comment<'s> {
pub source: &'s str, pub source: &'s str,
pub value: Vec<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -72,6 +74,9 @@ pub struct DiarySexp<'s> {
#[derive(Debug)] #[derive(Debug)]
pub struct Planning<'s> { pub struct Planning<'s> {
pub source: &'s str, pub source: &'s str,
pub scheduled: Option<Timestamp<'s>>,
pub deadline: Option<Timestamp<'s>>,
pub closed: Option<Timestamp<'s>>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -115,93 +120,106 @@ impl<'s> Paragraph<'s> {
} }
impl<'s> StandardProperties<'s> for Paragraph<'s> { impl<'s> StandardProperties<'s> for Paragraph<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for TableCell<'s> { impl<'s> StandardProperties<'s> for TableCell<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Comment<'s> { impl<'s> StandardProperties<'s> for Comment<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for VerseBlock<'s> { impl<'s> StandardProperties<'s> for VerseBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for CommentBlock<'s> { impl<'s> StandardProperties<'s> for CommentBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for ExampleBlock<'s> { impl<'s> StandardProperties<'s> for ExampleBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for ExportBlock<'s> { impl<'s> StandardProperties<'s> for ExportBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for SrcBlock<'s> { impl<'s> StandardProperties<'s> for SrcBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Clock<'s> { impl<'s> StandardProperties<'s> for Clock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for DiarySexp<'s> { impl<'s> StandardProperties<'s> for DiarySexp<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Planning<'s> { impl<'s> StandardProperties<'s> for Planning<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for FixedWidthArea<'s> { impl<'s> StandardProperties<'s> for FixedWidthArea<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for HorizontalRule<'s> { impl<'s> StandardProperties<'s> for HorizontalRule<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Keyword<'s> { impl<'s> StandardProperties<'s> for Keyword<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for BabelCall<'s> { impl<'s> StandardProperties<'s> for BabelCall<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for LatexEnvironment<'s> { impl<'s> StandardProperties<'s> for LatexEnvironment<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Comment<'s> {
pub fn get_value(&self) -> String {
// TODO: maybe we should handle parsing here instead of storing the parsing result in the AST since I imagine getting the value of comments won't be a common operation.
let final_size = self.value.iter().map(|line| line.len()).sum();
let mut ret = String::with_capacity(final_size);
for line in &self.value {
ret.push_str(line);
}
ret
}
}

12
src/types/macros.rs Normal file
View File

@@ -0,0 +1,12 @@
/// Write the implementation of From<> to convert a borrow of the type to an AstNode
macro_rules! to_ast_node {
($inp:ty, $enum:expr) => {
impl<'r, 's> From<$inp> for AstNode<'r, 's> {
fn from(value: $inp) -> Self {
$enum(value)
}
}
};
}
pub(crate) use to_ast_node;

View File

@@ -1,11 +1,14 @@
mod ast_node;
mod document; mod document;
mod element; mod element;
mod get_standard_properties; mod get_standard_properties;
mod greater_element; mod greater_element;
mod lesser_element; mod lesser_element;
mod macros;
mod object; mod object;
mod source; mod source;
mod standard_properties; mod standard_properties;
pub(crate) use ast_node::AstNode;
pub use document::Document; pub use document::Document;
pub use document::DocumentElement; pub use document::DocumentElement;
pub use document::Heading; pub use document::Heading;
@@ -15,11 +18,11 @@ pub use document::Section;
pub use document::TodoKeywordType; pub use document::TodoKeywordType;
pub use element::Element; pub use element::Element;
pub use get_standard_properties::GetStandardProperties; pub use get_standard_properties::GetStandardProperties;
pub use greater_element::CenterBlock;
pub use greater_element::CheckboxType; pub use greater_element::CheckboxType;
pub use greater_element::Drawer; pub use greater_element::Drawer;
pub use greater_element::DynamicBlock; pub use greater_element::DynamicBlock;
pub use greater_element::FootnoteDefinition; pub use greater_element::FootnoteDefinition;
pub use greater_element::GreaterBlock;
pub use greater_element::IndentationLevel; pub use greater_element::IndentationLevel;
pub use greater_element::NodeProperty; pub use greater_element::NodeProperty;
pub use greater_element::PlainList; pub use greater_element::PlainList;
@@ -28,8 +31,11 @@ pub use greater_element::PlainListItemCounter;
pub use greater_element::PlainListItemPreBlank; pub use greater_element::PlainListItemPreBlank;
pub use greater_element::PlainListType; pub use greater_element::PlainListType;
pub use greater_element::PropertyDrawer; pub use greater_element::PropertyDrawer;
pub use greater_element::QuoteBlock;
pub use greater_element::SpecialBlock;
pub use greater_element::Table; pub use greater_element::Table;
pub use greater_element::TableRow; pub use greater_element::TableRow;
pub use greater_element::TableRowType;
pub use lesser_element::BabelCall; pub use lesser_element::BabelCall;
pub use lesser_element::Clock; pub use lesser_element::Clock;
pub use lesser_element::Comment; pub use lesser_element::Comment;
@@ -51,14 +57,23 @@ pub use object::Bold;
pub use object::Citation; pub use object::Citation;
pub use object::CitationReference; pub use object::CitationReference;
pub use object::Code; pub use object::Code;
pub use object::Date;
pub use object::DayOfMonth;
pub use object::DayOfMonthInner;
pub use object::Entity; pub use object::Entity;
pub use object::ExportSnippet; pub use object::ExportSnippet;
pub use object::FootnoteReference; pub use object::FootnoteReference;
pub use object::Hour;
pub use object::HourInner;
pub use object::InlineBabelCall; pub use object::InlineBabelCall;
pub use object::InlineSourceBlock; pub use object::InlineSourceBlock;
pub use object::Italic; pub use object::Italic;
pub use object::LatexFragment; pub use object::LatexFragment;
pub use object::LineBreak; pub use object::LineBreak;
pub use object::Minute;
pub use object::MinuteInner;
pub use object::Month;
pub use object::MonthInner;
pub use object::Object; pub use object::Object;
pub use object::OrgMacro; pub use object::OrgMacro;
pub use object::PlainLink; pub use object::PlainLink;
@@ -66,13 +81,24 @@ pub use object::PlainText;
pub use object::RadioLink; pub use object::RadioLink;
pub use object::RadioTarget; pub use object::RadioTarget;
pub use object::RegularLink; pub use object::RegularLink;
pub use object::Repeater;
pub use object::RepeaterType;
pub use object::RepeaterWarningDelayValueType;
pub use object::StatisticsCookie; pub use object::StatisticsCookie;
pub use object::StrikeThrough; pub use object::StrikeThrough;
pub use object::Subscript; pub use object::Subscript;
pub use object::Superscript; pub use object::Superscript;
pub use object::Target; pub use object::Target;
pub use object::Time;
pub use object::TimeUnit;
pub use object::Timestamp; pub use object::Timestamp;
pub use object::TimestampRangeType;
pub use object::TimestampType;
pub use object::Underline; pub use object::Underline;
pub use object::Verbatim; pub use object::Verbatim;
pub use object::WarningDelay;
pub use object::WarningDelayType;
pub use object::Year;
pub use object::YearInner;
pub(crate) use source::SetSource; pub(crate) use source::SetSource;
pub use standard_properties::StandardProperties; pub use standard_properties::StandardProperties;

View File

@@ -1,6 +1,7 @@
use super::GetStandardProperties; use super::GetStandardProperties;
use super::StandardProperties; use super::StandardProperties;
// TODO: Why did we make Object implement PartialEq again? Was it just for tests?
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Object<'s> { pub enum Object<'s> {
Bold(Bold<'s>), Bold(Bold<'s>),
@@ -181,13 +182,260 @@ pub struct Superscript<'s> {
pub source: &'s str, pub source: &'s str,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub struct Timestamp<'s> { pub struct Timestamp<'s> {
pub source: &'s str, pub source: &'s str,
pub timestamp_type: TimestampType,
pub range_type: TimestampRangeType,
pub start: Option<Date<'s>>,
pub end: Option<Date<'s>>,
pub start_time: Option<Time<'s>>,
pub end_time: Option<Time<'s>>,
pub repeater: Option<Repeater>,
pub warning_delay: Option<WarningDelay>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum TimestampType {
Diary,
Active,
Inactive,
ActiveRange,
InactiveRange,
}
#[derive(Debug, PartialEq, Clone)]
pub enum TimestampRangeType {
None,
DateRange,
TimeRange,
}
pub type YearInner = u16;
pub type MonthInner = u8;
pub type DayOfMonthInner = u8;
pub type HourInner = u8;
pub type MinuteInner = u8;
#[derive(Debug, PartialEq, Clone)]
pub struct Year(YearInner);
#[derive(Debug, PartialEq, Clone)]
pub struct Month(MonthInner);
#[derive(Debug, PartialEq, Clone)]
pub struct DayOfMonth(DayOfMonthInner);
#[derive(Debug, PartialEq, Clone)]
pub struct Hour(HourInner);
#[derive(Debug, PartialEq, Clone)]
pub struct Minute(MinuteInner);
impl Year {
// TODO: Make a real error type instead of a boxed any error.
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
let year = source.parse::<YearInner>()?;
Ok(Year(year))
}
pub fn get_value(&self) -> YearInner {
self.0
}
}
impl Month {
// TODO: Make a real error type instead of a boxed any error.
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
let month = source.parse::<MonthInner>()?;
if month < 1 || month > 12 {
Err("Month exceeds possible range.")?;
}
Ok(Month(month))
}
pub fn get_value(&self) -> MonthInner {
self.0
}
}
impl DayOfMonth {
// TODO: Make a real error type instead of a boxed any error.
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
let day_of_month = source.parse::<DayOfMonthInner>()?;
if day_of_month < 1 || day_of_month > 31 {
Err("Day of month exceeds possible range.")?;
}
Ok(DayOfMonth(day_of_month))
}
pub fn get_value(&self) -> DayOfMonthInner {
self.0
}
}
impl Hour {
// TODO: Make a real error type instead of a boxed any error.
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
let hour = source.parse::<HourInner>()?;
if hour > 23 {
Err("Hour exceeds possible range.")?;
}
Ok(Hour(hour))
}
pub fn get_value(&self) -> HourInner {
self.0
}
}
impl Minute {
// TODO: Make a real error type instead of a boxed any error.
pub fn new<'s>(source: &'s str) -> Result<Self, Box<dyn std::error::Error>> {
let minute = source.parse::<MinuteInner>()?;
if minute > 59 {
Err("Minute exceeds possible range.")?;
}
Ok(Minute(minute))
}
pub fn get_value(&self) -> MinuteInner {
self.0
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Date<'s> {
year: Year,
month: Month,
day_of_month: DayOfMonth,
day_name: Option<&'s str>,
}
impl<'s> Date<'s> {
// TODO: Make a real error type instead of a boxed any error.
pub fn new(
year: Year,
month: Month,
day_of_month: DayOfMonth,
day_name: Option<&'s str>,
) -> Result<Self, Box<dyn std::error::Error>> {
// TODO: Does org-mode support non-gregorian calendars?
// TODO: Do I want to validate leap year?
match (month.get_value(), day_of_month.get_value()) {
(1, 1..=31) => {}
(2, 1..=29) => {}
(3, 1..=31) => {}
(4, 1..=30) => {}
(5, 1..=31) => {}
(6, 1..=30) => {}
(7, 1..=31) => {}
(8, 1..=31) => {}
(9, 1..=30) => {}
(10, 1..=31) => {}
(11, 1..=30) => {}
(12, 1..=31) => {}
_ => Err("Invalid day of month for the month.")?,
};
Ok(Date {
year,
month,
day_of_month,
day_name,
})
}
pub fn get_year(&self) -> &Year {
&self.year
}
pub fn get_month(&self) -> &Month {
&self.month
}
pub fn get_day_of_month(&self) -> &DayOfMonth {
&self.day_of_month
}
pub fn get_day_name(&self) -> Option<&'s str> {
self.day_name
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Time<'s> {
hour: Hour,
minute: Minute,
postfix: Option<&'s str>,
}
impl<'s> Time<'s> {
// TODO: Make a real error type instead of a boxed any error.
pub fn new(
hour: Hour,
minute: Minute,
postfix: Option<&'s str>,
) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Time {
hour,
minute,
postfix,
})
}
pub fn get_hour(&self) -> &Hour {
&self.hour
}
pub fn get_minute(&self) -> &Minute {
&self.minute
}
pub fn get_postfix(&self) -> Option<&'s str> {
self.postfix
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum RepeaterType {
Cumulative,
CatchUp,
Restart,
}
#[derive(Debug, PartialEq, Clone)]
pub enum WarningDelayType {
All,
First,
}
#[derive(Debug, PartialEq, Clone)]
pub enum TimeUnit {
Hour,
Day,
Week,
Month,
Year,
}
pub type RepeaterWarningDelayValueType = u16;
#[derive(Debug, PartialEq, Clone)]
pub struct Repeater {
pub repeater_type: RepeaterType,
pub value: RepeaterWarningDelayValueType,
pub unit: TimeUnit,
}
#[derive(Debug, PartialEq, Clone)]
pub struct WarningDelay {
pub warning_delay_type: WarningDelayType,
pub value: RepeaterWarningDelayValueType,
pub unit: TimeUnit,
} }
impl<'s> GetStandardProperties<'s> for Object<'s> { impl<'s> GetStandardProperties<'s> for Object<'s> {
fn get_standard_properties(&'s self) -> &'s dyn StandardProperties { fn get_standard_properties<'b>(&'b self) -> &'b dyn StandardProperties<'s> {
match self { match self {
Object::Bold(inner) => inner, Object::Bold(inner) => inner,
Object::Italic(inner) => inner, Object::Italic(inner) => inner,
@@ -221,163 +469,169 @@ impl<'s> GetStandardProperties<'s> for Object<'s> {
} }
impl<'s> StandardProperties<'s> for Bold<'s> { impl<'s> StandardProperties<'s> for Bold<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Italic<'s> { impl<'s> StandardProperties<'s> for Italic<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Underline<'s> { impl<'s> StandardProperties<'s> for Underline<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for StrikeThrough<'s> { impl<'s> StandardProperties<'s> for StrikeThrough<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Code<'s> { impl<'s> StandardProperties<'s> for Code<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Verbatim<'s> { impl<'s> StandardProperties<'s> for Verbatim<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for RegularLink<'s> { impl<'s> StandardProperties<'s> for RegularLink<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for RadioLink<'s> { impl<'s> StandardProperties<'s> for RadioLink<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for RadioTarget<'s> { impl<'s> StandardProperties<'s> for RadioTarget<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for PlainLink<'s> { impl<'s> StandardProperties<'s> for PlainLink<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for AngleLink<'s> { impl<'s> StandardProperties<'s> for AngleLink<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for OrgMacro<'s> { impl<'s> StandardProperties<'s> for OrgMacro<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Entity<'s> { impl<'s> StandardProperties<'s> for Entity<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for LatexFragment<'s> { impl<'s> StandardProperties<'s> for LatexFragment<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for ExportSnippet<'s> { impl<'s> StandardProperties<'s> for ExportSnippet<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for FootnoteReference<'s> { impl<'s> StandardProperties<'s> for FootnoteReference<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Citation<'s> { impl<'s> StandardProperties<'s> for Citation<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for CitationReference<'s> { impl<'s> StandardProperties<'s> for CitationReference<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for InlineBabelCall<'s> { impl<'s> StandardProperties<'s> for InlineBabelCall<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for InlineSourceBlock<'s> { impl<'s> StandardProperties<'s> for InlineSourceBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for LineBreak<'s> { impl<'s> StandardProperties<'s> for LineBreak<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Target<'s> { impl<'s> StandardProperties<'s> for Target<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for StatisticsCookie<'s> { impl<'s> StandardProperties<'s> for StatisticsCookie<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Subscript<'s> { impl<'s> StandardProperties<'s> for Subscript<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Superscript<'s> { impl<'s> StandardProperties<'s> for Superscript<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for Timestamp<'s> { impl<'s> StandardProperties<'s> for Timestamp<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> StandardProperties<'s> for PlainText<'s> { impl<'s> StandardProperties<'s> for PlainText<'s> {
fn get_source(&'s self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Timestamp<'s> {
pub fn get_raw_value(&self) -> &'s str {
self.source.trim_end()
}
}

View File

@@ -3,7 +3,7 @@ pub trait StandardProperties<'s> {
/// Get the slice of the entire AST node. /// Get the slice of the entire AST node.
/// ///
/// This corresponds to :begin to :end in upstream org-mode's standard properties. /// This corresponds to :begin to :end in upstream org-mode's standard properties.
fn get_source(&'s self) -> &'s str; fn get_source<'b>(&'b self) -> &'s str;
// Get the slice of the AST node's contents. // Get the slice of the AST node's contents.
// //