Compare commits
17 Commits
590e7fba0e
...
v0.1.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e673aa862e | ||
|
|
3b6659c5fd | ||
|
|
68a3f8b87e | ||
|
|
b1244de1dc | ||
|
|
e5a402ee1b | ||
|
|
d4a2ad4a7f | ||
|
|
3d1b2713ed | ||
|
|
60bec4695b | ||
|
|
d992947ff1 | ||
|
|
76fb24d1d1 | ||
|
|
b56318fbe4 | ||
|
|
8169499de3 | ||
|
|
29d9e76545 | ||
|
|
4d356b855e | ||
|
|
ae66d1bd89 | ||
|
|
c551938904 | ||
|
|
0fb80e3fee |
@@ -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"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Comment
|
||||
#
|
||||
# indented line
|
||||
# At the top of the file
|
||||
|
||||
|
||||
@@ -86,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;
|
||||
@@ -1122,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())?);
|
||||
@@ -1148,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())?);
|
||||
@@ -1173,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,
|
||||
@@ -1195,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())?);
|
||||
@@ -1245,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,
|
||||
@@ -1309,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())?);
|
||||
@@ -1333,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())?);
|
||||
|
||||
@@ -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"))]
|
||||
|
||||
@@ -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"))]
|
||||
|
||||
@@ -93,7 +93,6 @@ fn quote_block<'b, 'g, 'r, 's>(
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn special_block<'s>(
|
||||
name: &'s str,
|
||||
) -> impl for<'b, 'g, 'r> Fn(
|
||||
|
||||
@@ -63,6 +63,7 @@ pub(crate) fn planning<'b, 'g, 'r, 's>(
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PlanningTimestampType {
|
||||
Scheduled,
|
||||
Deadline,
|
||||
|
||||
@@ -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()),
|
||||
},
|
||||
))
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -94,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>,
|
||||
}
|
||||
|
||||
@@ -110,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
|
||||
@@ -193,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ 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;
|
||||
|
||||
Reference in New Issue
Block a user