21 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
19 changed files with 486 additions and 103 deletions

View File

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

View File

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

View File

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

View File

@@ -17,6 +17,7 @@ use crate::types::AngleLink;
use crate::types::AstNode;
use crate::types::BabelCall;
use crate::types::Bold;
use crate::types::CenterBlock;
use crate::types::CheckboxType;
use crate::types::Citation;
use crate::types::CitationReference;
@@ -40,7 +41,6 @@ use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference;
use crate::types::GetStandardProperties;
use crate::types::GreaterBlock;
use crate::types::Heading;
use crate::types::HorizontalRule;
use crate::types::Hour;
@@ -69,12 +69,14 @@ use crate::types::PlainText;
use crate::types::Planning;
use crate::types::PriorityCookie;
use crate::types::PropertyDrawer;
use crate::types::QuoteBlock;
use crate::types::RadioLink;
use crate::types::RadioTarget;
use crate::types::RegularLink;
use crate::types::RepeaterType;
use crate::types::RepeaterWarningDelayValueType;
use crate::types::Section;
use crate::types::SpecialBlock;
use crate::types::SrcBlock;
use crate::types::StandardProperties;
use crate::types::StatisticsCookie;
@@ -84,6 +86,7 @@ use crate::types::Superscript;
use crate::types::Table;
use crate::types::TableCell;
use crate::types::TableRow;
use crate::types::TableRowType;
use crate::types::Target;
use crate::types::Time;
use crate::types::TimeUnit;
@@ -329,7 +332,9 @@ fn compare_ast_node<'b, 's>(
AstNode::Paragraph(node) => compare_paragraph(source, emacs, node),
AstNode::PlainList(node) => compare_plain_list(source, emacs, node),
AstNode::PlainListItem(node) => compare_plain_list_item(source, emacs, node),
AstNode::GreaterBlock(node) => compare_greater_block(source, emacs, node),
AstNode::CenterBlock(node) => compare_center_block(source, emacs, node),
AstNode::QuoteBlock(node) => compare_quote_block(source, emacs, node),
AstNode::SpecialBlock(node) => compare_special_block(source, emacs, node),
AstNode::DynamicBlock(node) => compare_dynamic_block(source, emacs, node),
AstNode::FootnoteDefinition(node) => compare_footnote_definition(source, emacs, node),
AstNode::Comment(node) => compare_comment(source, emacs, node),
@@ -1011,17 +1016,90 @@ fn compare_plain_list_item<'b, 's>(
.into())
}
fn compare_greater_block<'b, 's>(
fn compare_center_block<'b, 's>(
source: &'s str,
emacs: &'b Token<'s>,
rust: &'b GreaterBlock<'s>,
rust: &'b CenterBlock<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let this_status = DiffStatus::Good;
let message = None;
// TODO: Special block compare :type :parameters
// Center and quote block has no additional properties
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
}
Ok(DiffResult {
status: this_status,
name: rust.get_elisp_name(),
message,
children: child_status,
rust_source: rust.get_source(),
emacs_token: emacs,
}
.into())
}
fn compare_quote_block<'b, 's>(
source: &'s str,
emacs: &'b Token<'s>,
rust: &'b QuoteBlock<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let this_status = DiffStatus::Good;
let message = None;
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
}
Ok(DiffResult {
status: this_status,
name: rust.get_elisp_name(),
message,
children: child_status,
rust_source: rust.get_source(),
emacs_token: emacs,
}
.into())
}
fn compare_special_block<'b, 's>(
source: &'s str,
emacs: &'b Token<'s>,
rust: &'b SpecialBlock<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let mut this_status = DiffStatus::Good;
let mut message = None;
// Compare type
let special_block_type =
get_property_quoted_string(emacs, ":type")?.ok_or("Special blocks should have a name.")?;
if special_block_type != rust.name {
this_status = DiffStatus::Bad;
message = Some(format!(
"Name mismatch (emacs != rust) {:?} != {:?}",
special_block_type, rust.name
));
}
// Compare parameters
let parameters = get_property_quoted_string(emacs, ":parameters")?;
match (parameters.as_ref(), rust.parameters) {
(None, None) => {}
(Some(emacs_parameters), Some(rust_parameters)) if emacs_parameters == rust_parameters => {}
_ => {
this_status = DiffStatus::Bad;
message = Some(format!(
"Parameters mismatch (emacs != rust) {:?} != {:?}",
parameters, rust.parameters
));
}
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
@@ -1045,9 +1123,33 @@ fn compare_dynamic_block<'b, 's>(
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let this_status = DiffStatus::Good;
let message = None;
// TODO: Compare :block-name :arguments
let mut this_status = DiffStatus::Good;
let mut message = None;
// Compare block-name
let block_name = get_property_quoted_string(emacs, ":block-name")?
.ok_or("Dynamic blocks should have a name.")?;
if block_name != rust.name {
this_status = DiffStatus::Bad;
message = Some(format!(
"Name mismatch (emacs != rust) {:?} != {:?}",
block_name, rust.name
));
}
// Compare arguments
let parameters = get_property_quoted_string(emacs, ":arguments")?;
match (parameters.as_ref(), rust.parameters) {
(None, None) => {}
(Some(emacs_parameters), Some(rust_parameters)) if emacs_parameters == rust_parameters => {}
_ => {
this_status = DiffStatus::Bad;
message = Some(format!(
"Parameters mismatch (emacs != rust) {:?} != {:?}",
parameters, rust.parameters
));
}
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
@@ -1071,9 +1173,20 @@ fn compare_footnote_definition<'b, 's>(
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let this_status = DiffStatus::Good;
let message = None;
// TODO: Compare :label :pre-blank
let mut this_status = DiffStatus::Good;
let mut message = None;
// TODO: Compare :pre-blank
// Compare label
let label = get_property_quoted_string(emacs, ":label")?
.ok_or("Footnote definitions should have a name.")?;
if label != rust.label {
this_status = DiffStatus::Bad;
message = Some(format!(
"Label mismatch (emacs != rust) {:?} != {:?}",
label, rust.label
));
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
@@ -1096,9 +1209,20 @@ fn compare_comment<'b, 's>(
rust: &'b Comment<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let child_status = Vec::new();
let this_status = DiffStatus::Good;
let message = None;
// TODO: Compare :value
let mut this_status = DiffStatus::Good;
let mut message = None;
// Compare value
let value =
get_property_quoted_string(emacs, ":value")?.ok_or("Comments should have a value.")?;
let rust_value = rust.get_value();
if value != rust_value {
this_status = DiffStatus::Bad;
message = Some(format!(
"Value mismatch (emacs != rust) {:?} != {:?}",
value, rust_value
));
}
Ok(DiffResult {
status: this_status,
@@ -1118,9 +1242,19 @@ fn compare_drawer<'b, 's>(
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let this_status = DiffStatus::Good;
let message = None;
// TODO: Compare :drawer-name
let mut this_status = DiffStatus::Good;
let mut message = None;
// Compare drawer-name
let name =
get_property_quoted_string(emacs, ":drawer-name")?.ok_or("Drawers should have a name.")?;
if name != rust.name {
this_status = DiffStatus::Bad;
message = Some(format!(
"Name mismatch (emacs != rust) {:?} != {:?}",
name, rust.name
));
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
@@ -1168,10 +1302,33 @@ fn compare_node_property<'b, 's>(
rust: &'b NodeProperty<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let child_status = Vec::new();
let this_status = DiffStatus::Good;
let message = None;
let mut this_status = DiffStatus::Good;
let mut message = None;
// TODO: Compare :key :value
// Compare key
let key =
get_property_quoted_string(emacs, ":key")?.ok_or("Node properties should have a key.")?;
if key != rust.name {
this_status = DiffStatus::Bad;
message = Some(format!(
"Key mismatch (emacs != rust) {:?} != {:?}",
key, rust.name
));
}
// Compare value
let value = get_property_quoted_string(emacs, ":value")?;
match (value.as_ref(), rust.value) {
(None, None) => {}
(Some(emacs_value), Some(rust_value)) if emacs_value == rust_value => {}
_ => {
this_status = DiffStatus::Bad;
message = Some(format!(
"Value mismatch (emacs != rust) {:?} != {:?}",
value, rust.value
));
}
}
Ok(DiffResult {
status: this_status,
@@ -1232,7 +1389,23 @@ fn compare_table<'b, 's>(
}
}
// TODO: Compare :type :value
// Compare type
let table_type = get_property_unquoted_atom(emacs, ":type")?.expect("Table should have a type");
if table_type != "org" {
this_status = DiffStatus::Bad;
message = Some(format!(
"Table type mismatch (emacs != rust) {:?} != {:?}",
table_type, "org"
));
}
// Compare value
let value = get_property(emacs, ":value")?;
if value.is_some() {
// I don't know what :value is for, but it seems to always be nil. This is here to alert me to value being non-nil so I can investigate.
this_status = DiffStatus::Bad;
message = Some(format!("Non-nil value {:?}", value))
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
@@ -1256,12 +1429,23 @@ fn compare_table_row<'b, 's>(
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let this_status = DiffStatus::Good;
let message = None;
let mut this_status = DiffStatus::Good;
let mut message = None;
// TODO: Compare :type
//
// :type is an unquoted atom of either standard or rule
// Compare type
let row_type = get_property_unquoted_atom(emacs, ":type")?;
let rust_row_type = rust.get_type();
match (row_type, &rust_row_type) {
(Some("standard"), TableRowType::Standard) => {}
(Some("rule"), TableRowType::Rule) => {}
_ => {
this_status = DiffStatus::Bad;
message = Some(format!(
"Type mismatch (emacs != rust) {:?} != {:?}",
row_type, rust_row_type
));
}
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);

View File

@@ -4,6 +4,7 @@ use crate::types::AngleLink;
use crate::types::AstNode;
use crate::types::BabelCall;
use crate::types::Bold;
use crate::types::CenterBlock;
use crate::types::Citation;
use crate::types::CitationReference;
use crate::types::Clock;
@@ -22,7 +23,6 @@ use crate::types::ExportSnippet;
use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference;
use crate::types::GreaterBlock;
use crate::types::Heading;
use crate::types::HorizontalRule;
use crate::types::InlineBabelCall;
@@ -42,10 +42,12 @@ use crate::types::PlainListItem;
use crate::types::PlainText;
use crate::types::Planning;
use crate::types::PropertyDrawer;
use crate::types::QuoteBlock;
use crate::types::RadioLink;
use crate::types::RadioTarget;
use crate::types::RegularLink;
use crate::types::Section;
use crate::types::SpecialBlock;
use crate::types::SrcBlock;
use crate::types::StatisticsCookie;
use crate::types::StrikeThrough;
@@ -83,7 +85,9 @@ impl<'r, 's> GetElispFact<'s> for AstNode<'r, 's> {
AstNode::Paragraph(inner) => *inner,
AstNode::PlainList(inner) => *inner,
AstNode::PlainListItem(inner) => *inner,
AstNode::GreaterBlock(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,
@@ -142,7 +146,9 @@ impl<'s> GetElispFact<'s> for Element<'s> {
match self {
Element::Paragraph(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::FootnoteDefinition(inner) => inner,
Element::Comment(inner) => inner,
@@ -230,13 +236,21 @@ impl<'s> ElispFact<'s> for PlainListItem<'s> {
}
}
impl<'s> ElispFact<'s> for GreaterBlock<'s> {
impl<'s> ElispFact<'s> for CenterBlock<'s> {
fn get_elisp_name<'b>(&'b self) -> Cow<'s, str> {
match self.name.to_lowercase().as_str() {
"center" => "center-block".into(),
"quote" => "quote-block".into(),
_ => "special-block".into(),
}
"center-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()
}
}

View File

@@ -24,7 +24,9 @@ impl<'r, 's> Iterator for AllAstNodeIter<'r, 's> {
AstNodeIter::Paragraph(ref mut i) => i.next(),
AstNodeIter::PlainList(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::FootnoteDefinition(ref mut i) => i.next(),
AstNodeIter::Comment(ref mut i) => i.next(),

View File

@@ -7,6 +7,7 @@ use crate::types::AngleLink;
use crate::types::AstNode;
use crate::types::BabelCall;
use crate::types::Bold;
use crate::types::CenterBlock;
use crate::types::Citation;
use crate::types::CitationReference;
use crate::types::Clock;
@@ -26,7 +27,6 @@ use crate::types::ExportSnippet;
use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference;
use crate::types::GreaterBlock;
use crate::types::Heading;
use crate::types::HorizontalRule;
use crate::types::InlineBabelCall;
@@ -46,10 +46,12 @@ use crate::types::PlainListItem;
use crate::types::PlainText;
use crate::types::Planning;
use crate::types::PropertyDrawer;
use crate::types::QuoteBlock;
use crate::types::RadioLink;
use crate::types::RadioTarget;
use crate::types::RegularLink;
use crate::types::Section;
use crate::types::SpecialBlock;
use crate::types::SrcBlock;
use crate::types::StatisticsCookie;
use crate::types::StrikeThrough;
@@ -78,7 +80,9 @@ pub(crate) enum AstNodeIter<'r, 's> {
Paragraph(ParagraphIter<'r, 's>),
PlainList(PlainListIter<'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>),
FootnoteDefinition(FootnoteDefinitionIter<'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::PlainList(inner) => AstNodeIter::PlainList(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::FootnoteDefinition(inner) => {
AstNodeIter::FootnoteDefinition(inner.into_iter())
@@ -232,8 +238,18 @@ multi_field_iter!(
std::slice::Iter<'r, Element<'s>>
);
children_iter!(
GreaterBlock<'s>,
GreaterBlockIter,
CenterBlock<'s>,
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>>
);
children_iter!(

View File

@@ -1,18 +1,19 @@
use nom::branch::alt;
use nom::bytes::complete::is_not;
use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::line_ending;
use nom::character::complete::space0;
use nom::character::complete::space1;
use nom::combinator::eof;
use nom::combinator::not;
use nom::combinator::opt;
use nom::combinator::recognize;
use nom::multi::many0;
use nom::multi::many_till;
use nom::sequence::preceded;
use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::get_consumed;
use super::util::org_line_ending;
use crate::context::parser_with_context;
use crate::context::ContextElement;
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 comment_line_matcher = parser_with_context!(comment_line)(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
let (remaining, _first_line) = comment_line_matcher(input)?;
let (remaining, _remaining_lines) =
let (remaining, first_line) = comment_line_matcher(input)?;
let (remaining, mut remaining_lines) =
many0(preceded(not(exit_matcher), comment_line_matcher))(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((
remaining,
Comment {
source: source.into(),
value,
},
))
}
@@ -57,14 +72,13 @@ fn comment_line<'b, 'g, 'r, 's>(
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
let (remaining, _indent) = space0(input)?;
let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple((
tag("#"),
opt(tuple((space1, is_not("\r\n")))),
alt((line_ending, eof)),
))(remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, source))
let (remaining, _) = tuple((space0, tag("#")))(input)?;
if let Ok((remaining, line_break)) = org_line_ending(remaining) {
return Ok((remaining, line_break));
}
let (remaining, _) = tag(" ")(remaining)?;
let (remaining, value) = recognize(many_till(anychar, org_line_ending))(remaining)?;
Ok((remaining, value))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@@ -2,6 +2,7 @@ use nom::branch::alt;
use nom::bytes::complete::is_not;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar;
use nom::character::complete::line_ending;
use nom::character::complete::space0;
use nom::character::complete::space1;
@@ -9,6 +10,7 @@ use nom::combinator::consumed;
use nom::combinator::eof;
use nom::combinator::not;
use nom::combinator::opt;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::multi::many0;
use nom::multi::many_till;
@@ -47,10 +49,11 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>(
}
start_of_line(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))),
name,
opt(tuple((space1, parameters))),
space0,
line_ending,
))(remaining)?;
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"))]
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"))]

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 element) = match alt((
map(plain_list_matcher, Element::PlainList),
map(greater_block_matcher, Element::GreaterBlock),
greater_block_matcher,
map(dynamic_block_matcher, Element::DynamicBlock),
map(footnote_definition_matcher, Element::FootnoteDefinition),
map(comment_matcher, Element::Comment),

View File

@@ -1,6 +1,7 @@
use nom::branch::alt;
use nom::bytes::complete::is_not;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar;
use nom::character::complete::line_ending;
use nom::character::complete::space0;
use nom::character::complete::space1;
@@ -8,6 +9,8 @@ use nom::combinator::consumed;
use nom::combinator::eof;
use nom::combinator::not;
use nom::combinator::opt;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::combinator::verify;
use nom::multi::many0;
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::get_consumed;
use crate::parser::util::start_of_line;
use crate::types::CenterBlock;
use crate::types::Element;
use crate::types::GreaterBlock;
use crate::types::Paragraph;
use crate::types::QuoteBlock;
use crate::types::SetSource;
use crate::types::SpecialBlock;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn greater_block<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, GreaterBlock<'s>> {
// TODO: Do I need to differentiate between different greater block types.
) -> Res<OrgSource<'s>, Element<'s>> {
start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, name)) = tuple((
@@ -52,22 +56,97 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
}
}),
))(remaining)?;
let context_name = match Into::<&str>::into(name).to_lowercase().as_str() {
"center" => "center block".to_owned(),
"quote" => "quote block".to_owned(),
name @ _ => format!("special block {}", name),
let name = Into::<&str>::into(name);
let (remaining, element) = match name.to_lowercase().as_str() {
"center" => center_block(context, remaining, input)?,
"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(
"Cannot nest objects of the same element".into(),
))));
}
let exit_with_name = greater_block_end(name.into());
let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?;
let (remaining, _nl) = line_ending(remaining)?;
let exit_with_name = greater_block_end(name);
let (remaining, _nl) = tuple((space0, line_ending))(input)?;
let contexts = [
ContextElement::ConsumeTrailingWhitespace(true),
ContextElement::Context(context_name.as_str()),
ContextElement::Context(context_name),
ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Alpha,
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 = parser_context.with_additional_node(&contexts[1]);
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 exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
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
let source = get_consumed(input, remaining);
Ok((
remaining,
GreaterBlock {
source: source.into(),
name: name.into(),
parameters: parameters.map(|val| Into::<&str>::into(val)),
children,
},
))
let source = get_consumed(original_input, remaining);
Ok((remaining, (Into::<&str>::into(source), children)))
}
#[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"))]
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 {

View File

@@ -63,6 +63,7 @@ pub(crate) fn planning<'b, 'g, 'r, 's>(
))
}
#[derive(Debug)]
enum PlanningTimestampType {
Scheduled,
Deadline,

View File

@@ -101,7 +101,7 @@ fn node_property<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'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((
start_of_line,
space0,
@@ -120,6 +120,7 @@ fn node_property<'b, 'g, 'r, 's>(
remaining,
NodeProperty {
source: source.into(),
name: Into::<&str>::into(name),
value: None,
},
))
@@ -132,6 +133,7 @@ fn node_property<'b, 'g, 'r, 's>(
remaining,
NodeProperty {
source: source.into(),
name: Into::<&str>::into(name),
value: Some(value.into()),
},
))

View File

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

View File

@@ -1,4 +1,7 @@
use super::macros::to_ast_node;
use super::CenterBlock;
use super::QuoteBlock;
use super::SpecialBlock;
use crate::types::AngleLink;
use crate::types::BabelCall;
use crate::types::Bold;
@@ -22,7 +25,6 @@ use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference;
use crate::types::GetStandardProperties;
use crate::types::GreaterBlock;
use crate::types::Heading;
use crate::types::HorizontalRule;
use crate::types::InlineBabelCall;
@@ -69,7 +71,9 @@ pub enum AstNode<'r, 's> {
Paragraph(&'r Paragraph<'s>),
PlainList(&'r PlainList<'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>),
FootnoteDefinition(&'r FootnoteDefinition<'s>),
Comment(&'r Comment<'s>),
@@ -136,7 +140,9 @@ impl<'r, 's> From<&'r Element<'s>> for AstNode<'r, 's> {
match value {
Element::Paragraph(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::FootnoteDefinition(inner) => inner.into(),
Element::Comment(inner) => inner.into(),
@@ -200,7 +206,9 @@ to_ast_node!(&'r Section<'s>, AstNode::Section);
to_ast_node!(&'r Paragraph<'s>, AstNode::Paragraph);
to_ast_node!(&'r PlainList<'s>, AstNode::PlainList);
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 FootnoteDefinition<'s>, AstNode::FootnoteDefinition);
to_ast_node!(&'r Comment<'s>, AstNode::Comment);
@@ -260,7 +268,9 @@ impl<'r, 's> GetStandardProperties<'s> for AstNode<'r, 's> {
AstNode::Paragraph(inner) => *inner,
AstNode::PlainList(inner) => *inner,
AstNode::PlainListItem(inner) => *inner,
AstNode::GreaterBlock(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,

View File

@@ -1,6 +1,5 @@
use super::greater_element::DynamicBlock;
use super::greater_element::FootnoteDefinition;
use super::greater_element::GreaterBlock;
use super::greater_element::PlainList;
use super::greater_element::PropertyDrawer;
use super::greater_element::Table;
@@ -19,16 +18,21 @@ use super::lesser_element::Paragraph;
use super::lesser_element::Planning;
use super::lesser_element::SrcBlock;
use super::lesser_element::VerseBlock;
use super::CenterBlock;
use super::Drawer;
use super::GetStandardProperties;
use super::QuoteBlock;
use super::SetSource;
use super::SpecialBlock;
use super::StandardProperties;
#[derive(Debug)]
pub enum Element<'s> {
Paragraph(Paragraph<'s>),
PlainList(PlainList<'s>),
GreaterBlock(GreaterBlock<'s>),
CenterBlock(CenterBlock<'s>),
QuoteBlock(QuoteBlock<'s>),
SpecialBlock(SpecialBlock<'s>),
DynamicBlock(DynamicBlock<'s>),
FootnoteDefinition(FootnoteDefinition<'s>),
Comment(Comment<'s>),
@@ -56,7 +60,9 @@ impl<'s> SetSource<'s> for Element<'s> {
match self {
Element::Paragraph(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::FootnoteDefinition(obj) => obj.source = source,
Element::Comment(obj) => obj.source = source,
@@ -85,7 +91,9 @@ impl<'s> GetStandardProperties<'s> for Element<'s> {
match self {
Element::Paragraph(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::FootnoteDefinition(inner) => inner,
Element::Comment(inner) => inner,

View File

@@ -44,7 +44,19 @@ pub enum CheckboxType {
}
#[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 name: &'s str,
pub parameters: Option<&'s str>,
@@ -82,6 +94,7 @@ pub struct PropertyDrawer<'s> {
#[derive(Debug)]
pub struct NodeProperty<'s> {
pub source: &'s str,
pub name: &'s str,
pub value: Option<&'s str>,
}
@@ -98,6 +111,12 @@ pub struct TableRow<'s> {
pub children: Vec<TableCell<'s>>,
}
#[derive(Debug)]
pub enum TableRowType {
Standard,
Rule,
}
impl<'s> StandardProperties<'s> for PlainList<'s> {
fn get_source<'b>(&'b self) -> &'s str {
self.source
@@ -110,7 +129,19 @@ impl<'s> StandardProperties<'s> for PlainListItem<'s> {
}
}
impl<'s> StandardProperties<'s> for GreaterBlock<'s> {
impl<'s> StandardProperties<'s> for CenterBlock<'s> {
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
}
@@ -169,3 +200,13 @@ impl<'s> PlainListItem<'s> {
.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

@@ -12,6 +12,7 @@ pub struct Paragraph<'s> {
#[derive(Debug)]
pub struct Comment<'s> {
pub source: &'s str,
pub value: Vec<&'s str>,
}
#[derive(Debug)]
@@ -209,3 +210,16 @@ impl<'s> StandardProperties<'s> for LatexEnvironment<'s> {
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
}
}

View File

@@ -18,11 +18,11 @@ pub use document::Section;
pub use document::TodoKeywordType;
pub use element::Element;
pub use get_standard_properties::GetStandardProperties;
pub use greater_element::CenterBlock;
pub use greater_element::CheckboxType;
pub use greater_element::Drawer;
pub use greater_element::DynamicBlock;
pub use greater_element::FootnoteDefinition;
pub use greater_element::GreaterBlock;
pub use greater_element::IndentationLevel;
pub use greater_element::NodeProperty;
pub use greater_element::PlainList;
@@ -31,8 +31,11 @@ pub use greater_element::PlainListItemCounter;
pub use greater_element::PlainListItemPreBlank;
pub use greater_element::PlainListType;
pub use greater_element::PropertyDrawer;
pub use greater_element::QuoteBlock;
pub use greater_element::SpecialBlock;
pub use greater_element::Table;
pub use greater_element::TableRow;
pub use greater_element::TableRowType;
pub use lesser_element::BabelCall;
pub use lesser_element::Clock;
pub use lesser_element::Comment;