19 Commits

Author SHA1 Message Date
Tom Alexander
ee92049e5d Merge branch 'special_block'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-31 19:19:29 -04:00
Tom Alexander
510985e97c Fix greater blocks by preventing nesting even if they are not the immediate parent. 2023-08-31 19:17:46 -04:00
Tom Alexander
949d0989f4 Add a test showing the current parser is broken with deeply nested greater blocks. 2023-08-31 19:15:28 -04:00
Tom Alexander
2a4d22bdd4 Add test for nesting of greater blocks.
This shows that greater blocks of different types can be directly nested even if they are both special blocks (as long as they are different special blocks).
2023-08-31 19:09:38 -04:00
Tom Alexander
7a903acedc Support special blocks in compare. 2023-08-31 19:04:44 -04:00
Tom Alexander
5171326d63 Create a test for special blocks. 2023-08-31 19:02:18 -04:00
Tom Alexander
67f79aeb51 Remove context from detect plain list.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-31 18:53:28 -04:00
Tom Alexander
b2383d9f93 Fix footnote definition performance.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
We were re-parsing each footnote definition in the footnote definition exit matcher which causes their contents to get re-parsed. This compounds with long lists of footnote definitions.
2023-08-31 18:47:23 -04:00
Tom Alexander
9e2a323f6f Merge branch 'trailing_whitespace_at_end_of_file'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-31 17:29:52 -04:00
Tom Alexander
0fcb3f73f9 Move handling of contentless item to handle contentless description item. 2023-08-31 17:25:42 -04:00
Tom Alexander
bfc9e7f58d When re-parsing last item in list, only disable white space consumption for last element in item. 2023-08-31 17:08:21 -04:00
Tom Alexander
b5f0521b56 Only consume trailing whitespace when not exiting for all objects. 2023-08-31 15:45:31 -04:00
Tom Alexander
2048d8f0b6 Support comments at the end of the line in diary sexp.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-31 15:31:54 -04:00
Tom Alexander
466716881e Fix macros consuming whitespace even when the exit matcher is calling for an exit.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-31 15:11:29 -04:00
Tom Alexander
eb9c582fa5 '>' is allowed as a post character in text markup.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-29 23:57:15 -04:00
Tom Alexander
214e895d85 Support backslash at the end of a macro. 2023-08-29 23:42:16 -04:00
Tom Alexander
db3086743c Publish version 0.1.4.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-29 23:28:17 -04:00
Tom Alexander
207a0546b0 Run full test suite despite default feature selection.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-29 23:23:31 -04:00
Tom Alexander
e9480fd156 Disable the compare tests when the compare feature is disabled.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-29 23:17:46 -04:00
38 changed files with 239 additions and 105 deletions

View File

@@ -110,7 +110,16 @@ spec:
- build-image
params:
- name: args
value: [--no-fail-fast, --lib, --test, test_loader]
value:
[
--no-default-features,
--features,
compare,
--no-fail-fast,
--lib,
--test,
test_loader,
]
- name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
finally:

View File

@@ -1,6 +1,6 @@
[package]
name = "organic"
version = "0.1.3"
version = "0.1.4"
authors = ["Tom Alexander <tom@fizz.buzz>"]
description = "An org-mode parser."
edition = "2021"
@@ -13,9 +13,7 @@ resolver = "2"
include = [
"LICENSE",
"**/*.rs",
"Cargo.toml",
"tests/*",
"org_mode_samples/"
"Cargo.toml"
]
[lib]

View File

@@ -35,12 +35,12 @@ clean:
.PHONY: test
test:
> cargo test --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
> cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
.PHONY: dockertest
dockertest:
> $(MAKE) -C docker/organic_test
> docker run --init --rm -i -t -v "$$(readlink -f ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
> docker run --init --rm -i -t -v "$$(readlink -f ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
.PHONY: dockerclean
dockerclean:
@@ -49,11 +49,11 @@ dockerclean:
.PHONY: integrationtest
integrationtest:
> cargo test --no-fail-fast --test test_loader -- --test-threads $(TESTJOBS)
> cargo test --no-default-features --features compare --no-fail-fast --test test_loader -- --test-threads $(TESTJOBS)
.PHONY: unittest
unittest:
> cargo test --lib -- --test-threads $(TESTJOBS)
> cargo test --no-default-features --lib -- --test-threads $(TESTJOBS)
.PHONY: jaeger
jaeger:

View File

@@ -1,10 +1,16 @@
#[cfg(feature = "compare")]
use std::env;
#[cfg(feature = "compare")]
use std::fs::File;
#[cfg(feature = "compare")]
use std::io::Write;
#[cfg(feature = "compare")]
use std::path::Path;
#[cfg(feature = "compare")]
use walkdir::WalkDir;
#[cfg(feature = "compare")]
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let destination = Path::new(&out_dir).join("tests.rs");
@@ -31,6 +37,10 @@ fn main() {
}
}
#[cfg(not(feature = "compare"))]
fn main() {}
#[cfg(feature = "compare")]
fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
let test_name = test
.path()
@@ -55,6 +65,7 @@ fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
.unwrap();
}
#[cfg(feature = "compare")]
fn write_header(test_file: &mut File) {
write!(
test_file,
@@ -70,6 +81,7 @@ use organic::parser::sexp::sexp_with_padding;
.unwrap();
}
#[cfg(feature = "compare")]
fn is_expect_fail(name: &str) -> Option<&str> {
match name {
"autogen_greater_element_drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."),

View File

@@ -29,7 +29,7 @@ endif
.PHONY: run
run: build
docker run --rm --init -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME) --no-fail-fast --lib --test test_loader
docker run --rm --init -v "$$(readlink -f ../../):/source:ro" --workdir=/source --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target $(IMAGE_NAME) --no-default-features --features compare --no-fail-fast --lib --test test_loader
.PHONY: shell
shell: build

View File

@@ -0,0 +1,18 @@
#+begin_defun
foo
#+begin_lorem
,#+begin_center
bar
,#+end_center
ipsum
#+end_lorem
baz
#+end_defun
#+begin_center
#+begin_quote
#+begin_center
lorem
#+end_center
#+end_quote
#+end_center

View File

@@ -0,0 +1,12 @@
#+begin_defun
foo
#+begin_lorem
ipsum
#+end_lorem
bar
#+begin_center
#+begin_quote
baz
#+end_quote
#+end_center
#+end_defun

View File

@@ -0,0 +1,5 @@
#+begin_defun
foo
{{{bar(baz)}}}
#+end_defun

View File

@@ -0,0 +1 @@
- {{{foo(bar)}}} :: baz

View File

@@ -0,0 +1,2 @@
- foo

View File

@@ -0,0 +1 @@
%%(foo bar) ; baz

View File

@@ -0,0 +1 @@
foo ==>bar=.

View File

@@ -52,7 +52,7 @@ function launch_container {
set -euo pipefail
IFS=\$'\n\t'
cargo test --no-fail-fast --lib --test test_loader "$test" -- --show-output
cargo test --no-default-features --features compare --no-fail-fast --lib --test test_loader "$test" -- --show-output
EOF
)

View File

@@ -12,7 +12,7 @@ function main {
local test
while read test; do
cargo test --no-fail-fast --test test_loader "$test" -- --show-output
cargo test --no-default-features --features compare --no-fail-fast --test test_loader "$test" -- --show-output
done<<<"$test_names"
}

View File

@@ -737,7 +737,7 @@ fn compare_greater_block<'s>(
let emacs_name = match rust.name.to_lowercase().as_str() {
"center" => "center-block",
"quote" => "quote-block",
_ => todo!(),
_ => "special-block",
};
if assert_name(emacs, emacs_name).is_err() {
this_status = DiffStatus::Bad;

View File

@@ -5,6 +5,7 @@ use nom::combinator::recognize;
use nom::multi::many_till;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::Res;
use crate::parser::exiting::ExitClass;
@@ -26,6 +27,8 @@ pub fn angle_link<'r, 's>(
let (remaining, _separator) = tag(":")(remaining)?;
let (remaining, path) = path_angle(context, remaining)?;
let (remaining, _) = tag(">")(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -2,7 +2,6 @@ use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar;
use nom::character::complete::space0;
use nom::combinator::opt;
use nom::combinator::recognize;
use nom::combinator::verify;
@@ -14,6 +13,7 @@ use nom::sequence::tuple;
use super::citation_reference::must_balance_bracket;
use super::org_source::BracketDepth;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::CustomError;
use crate::error::Res;
@@ -48,7 +48,8 @@ pub fn citation<'r, 's>(
parser_with_context!(global_suffix)(context),
))))(remaining)?;
let (remaining, _) = tag("]")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -1,9 +1,12 @@
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::line_ending;
use nom::character::complete::space0;
use nom::combinator::eof;
use nom::combinator::opt;
use nom::combinator::recognize;
use nom::multi::many_till;
use nom::sequence::tuple;
use super::org_source::OrgSource;
@@ -24,6 +27,11 @@ pub fn diary_sexp<'r, 's>(
let (remaining, _clock) = tag("%%")(remaining)?;
let (remaining, _gap_whitespace) = space0(remaining)?;
let (remaining, _sexp) = recognize(sexp)(remaining)?;
let (remaining, _trailing_comment) = opt(tuple((
space0,
tag(";"),
many_till(anychar, alt((line_ending, eof))),
)))(remaining)?;
let (remaining, _trailing_whitespace) =
recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?;

View File

@@ -128,7 +128,7 @@ fn _detect_element<'r, 's>(
input: OrgSource<'s>,
can_be_paragraph: bool,
) -> Res<OrgSource<'s>, ()> {
if detect_plain_list(context, input).is_ok() {
if detect_plain_list(input).is_ok() {
return Ok((input, ()));
}
if _element(context, input, can_be_paragraph).is_ok() {

View File

@@ -2,12 +2,12 @@ use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::satisfy;
use nom::character::complete::space0;
use nom::combinator::eof;
use nom::combinator::peek;
use nom::combinator::recognize;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::CustomError;
use crate::error::MyError;
@@ -440,7 +440,8 @@ pub fn entity<'r, 's>(
let (remaining, _) = tag("\\")(input)?;
let (remaining, entity_name) = name(context, remaining)?;
let (remaining, _) = alt((tag("{}"), peek(recognize(entity_end))))(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((

View File

@@ -8,6 +8,7 @@ use nom::multi::many_till;
use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::Res;
use crate::parser::exiting::ExitClass;
@@ -35,6 +36,8 @@ pub fn export_snippet<'r, 's>(
parser_with_context!(contents)(&parser_context),
)))(remaining)?;
let (remaining, _) = tag("@@")(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -80,21 +80,10 @@ fn footnote_definition_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let allow_nesting_context =
context.with_additional_node(ContextElement::Context("allow nesting footnotes"));
let footnote_definition_matcher = parser_with_context!(footnote_definition)(
if immediate_in_section(context, "footnote definition") {
&allow_nesting_context
} else {
context
},
);
let maybe_consume_trailing_whitespace_matcher =
parser_with_context!(maybe_consume_trailing_whitespace)(context);
let (remaining, source) = alt((
recognize(tuple((
maybe_consume_trailing_whitespace_matcher,
footnote_definition_matcher,
parser_with_context!(maybe_consume_trailing_whitespace)(context),
detect_footnote_definition,
))),
recognize(tuple((
start_of_line,
@@ -107,6 +96,12 @@ fn footnote_definition_end<'r, 's>(
Ok((remaining, source))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn detect_footnote_definition<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
tuple((start_of_line, tag_no_case("[fn:"), label, tag("]")))(input)?;
Ok((input, ()))
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,13 +1,13 @@
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::space0;
use nom::combinator::verify;
use nom::multi::many_till;
use super::org_source::BracketDepth;
use super::org_source::OrgSource;
use super::parser_context::ContextElement;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::CustomError;
use crate::error::MyError;
@@ -54,7 +54,8 @@ fn anonymous_footnote<'r, 's>(
)(remaining)?;
let (remaining, _) = tag("]")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,
@@ -89,7 +90,8 @@ fn inline_footnote<'r, 's>(
)(remaining)?;
let (remaining, _) = tag("]")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,
@@ -103,13 +105,14 @@ fn inline_footnote<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn footnote_reference_only<'r, 's>(
_context: Context<'r, 's>,
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, FootnoteReference<'s>> {
let (remaining, _) = tag_no_case("[fn:")(input)?;
let (remaining, label_contents) = label(remaining)?;
let (remaining, _) = tag("]")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -12,6 +12,7 @@ use nom::multi::many_till;
use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::in_section;
use super::Context;
use crate::error::CustomError;
use crate::error::MyError;
@@ -26,7 +27,6 @@ use crate::parser::source::SetSource;
use crate::parser::util::blank_line;
use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed;
use crate::parser::util::immediate_in_section;
use crate::parser::util::start_of_line;
use crate::parser::Element;
use crate::parser::Paragraph;
@@ -49,11 +49,11 @@ pub fn greater_block<'r, 's>(
}),
))(remaining)?;
let context_name = match Into::<&str>::into(name).to_lowercase().as_str() {
"center" => "center block",
"quote" => "quote block",
_ => "greater block",
"center" => "center block".to_owned(),
"quote" => "quote block".to_owned(),
name @ _ => format!("special block {}", name),
};
if immediate_in_section(context, context_name) {
if in_section(context, context_name.as_str()) {
return Err(nom::Err::Error(CustomError::MyError(MyError(
"Cannot nest objects of the same element".into(),
))));
@@ -63,7 +63,7 @@ pub fn greater_block<'r, 's>(
let (remaining, _nl) = line_ending(remaining)?;
let parser_context = context
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
.with_additional_node(ContextElement::Context(context_name))
.with_additional_node(ContextElement::Context(context_name.as_str()))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Alpha,
exit_matcher: &exit_with_name,

View File

@@ -4,7 +4,6 @@ use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar;
use nom::character::complete::line_ending;
use nom::character::complete::one_of;
use nom::character::complete::space0;
use nom::combinator::opt;
use nom::combinator::recognize;
use nom::combinator::verify;
@@ -12,6 +11,7 @@ use nom::multi::many_till;
use super::org_source::BracketDepth;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::CustomError;
use crate::error::MyError;
@@ -34,7 +34,8 @@ pub fn inline_babel_call<'r, 's>(
let (remaining, _header1) = opt(parser_with_context!(header)(context))(remaining)?;
let (remaining, _argument) = argument(context, remaining)?;
let (remaining, _header2) = opt(parser_with_context!(header)(context))(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -4,7 +4,6 @@ use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar;
use nom::character::complete::line_ending;
use nom::character::complete::one_of;
use nom::character::complete::space0;
use nom::combinator::opt;
use nom::combinator::recognize;
use nom::combinator::verify;
@@ -14,6 +13,7 @@ use tracing::span;
use super::org_source::BracketDepth;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::CustomError;
use crate::error::MyError;
@@ -35,7 +35,8 @@ pub fn inline_source_block<'r, 's>(
let (remaining, _) = lang(context, remaining)?;
let (remaining, _header1) = opt(parser_with_context!(header)(context))(remaining)?;
let (remaining, _body) = body(context, remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -5,7 +5,6 @@ use nom::character::complete::anychar;
use nom::character::complete::line_ending;
use nom::character::complete::none_of;
use nom::character::complete::one_of;
use nom::character::complete::space0;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::combinator::verify;
@@ -14,6 +13,7 @@ use nom::multi::many_till;
use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::CustomError;
use crate::error::MyError;
@@ -36,7 +36,8 @@ pub fn latex_fragment<'r, 's>(
parser_with_context!(dollar_char_fragment)(context),
parser_with_context!(bordered_dollar_fragment)(context),
))(input)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -1,6 +1,5 @@
use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::space0;
use nom::combinator::not;
use nom::combinator::opt;
use nom::combinator::peek;
@@ -9,7 +8,9 @@ use nom::multi::many0;
use nom::multi::separated_list0;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::CustomError;
use crate::error::Res;
use crate::parser::object::OrgMacro;
use crate::parser::parser_with_context::parser_with_context;
@@ -25,7 +26,8 @@ pub fn org_macro<'r, 's>(
let (remaining, macro_name) = org_macro_name(context, remaining)?;
let (remaining, macro_args) = opt(parser_with_context!(org_macro_args)(context))(remaining)?;
let (remaining, _) = tag("}}}")(remaining)?;
let (remaining, _trailing_whitespace) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
@@ -86,6 +88,11 @@ fn org_macro_arg<'r, 's>(
}
if next_char == '\\' {
escaping = true;
if peek(tag::<_, _, CustomError<_>>(")"))(new_remaining).is_ok() {
// Special case for backslash at the end of a macro
remaining = new_remaining;
break;
}
}
if next_char == ',' || next_char == ')' {
break;

View File

@@ -38,10 +38,7 @@ use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting;
use crate::parser::util::start_of_line;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn detect_plain_list<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, ()> {
pub fn detect_plain_list<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
if verify(
tuple((
start_of_line,
@@ -69,6 +66,7 @@ pub fn plain_list<'r, 's>(
) -> Res<OrgSource<'s>, PlainList<'s>> {
let parser_context = context
.with_additional_node(ContextElement::Context("plain list"))
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta,
exit_matcher: &plain_list_end,
@@ -143,29 +141,32 @@ pub fn plain_list_item<'r, 's>(
Into::<&str>::into(bull) != "*" || indent_level > 0
})(remaining)?;
let maybe_contentless_item: Res<OrgSource<'_>, OrgSource<'_>> = eof(remaining);
let (remaining, maybe_tag) = opt(tuple((
space1,
parser_with_context!(item_tag)(context),
tag(" ::"),
)))(remaining)?;
let maybe_contentless_item: Res<OrgSource<'_>, OrgSource<'_>> =
peek(recognize(tuple((many0(blank_line), eof))))(remaining);
match maybe_contentless_item {
Ok((rem, _ws)) => {
let source = get_consumed(input, rem);
Ok((_rem, _ws)) => {
let (remaining, _trailing_ws) = opt(blank_line)(remaining)?;
let source = get_consumed(input, remaining);
return Ok((
rem,
remaining,
PlainListItem {
source: source.into(),
indentation: indent_level,
bullet: bull.into(),
tag: Vec::new(),
tag: maybe_tag
.map(|(_ws, item_tag, _divider)| item_tag)
.unwrap_or(Vec::new()),
children: Vec::new(),
},
));
}
Err(_) => {}
};
let (remaining, maybe_tag) = opt(tuple((
space1,
parser_with_context!(item_tag)(context),
tag(" ::"),
)))(remaining)?;
let (remaining, _ws) = item_tag_post_gap(context, remaining)?;
let exit_matcher = plain_list_item_end(indent_level);
let parser_context = context
@@ -175,14 +176,24 @@ pub fn plain_list_item<'r, 's>(
exit_matcher: &exit_matcher,
}));
let (remaining, (children, _exit_contents)) = many_till(
parser_with_context!(element(true))(&parser_context),
alt((
peek(recognize(tuple((start_of_line, many0(blank_line), eof)))),
parser_with_context!(exit_matcher_parser)(&parser_context),
)),
let (mut remaining, (mut children, _exit_contents)) = many_till(
include_input(parser_with_context!(element(true))(&parser_context)),
parser_with_context!(exit_matcher_parser)(&parser_context),
)(remaining)?;
if !children.is_empty() && !context.should_consume_trailing_whitespace() {
let final_item_context =
parser_context.with_additional_node(ContextElement::ConsumeTrailingWhitespace(false));
let (final_child_start, _original_final_child) = children
.pop()
.expect("if-statement already checked that children was non-empty.");
let (remain, reparsed_final_element) = include_input(parser_with_context!(element(true))(
&final_item_context,
))(final_child_start)?;
remaining = remain;
children.push(reparsed_final_element);
}
let (remaining, _trailing_ws) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
@@ -196,11 +207,23 @@ pub fn plain_list_item<'r, 's>(
tag: maybe_tag
.map(|(_ws, item_tag, _divider)| item_tag)
.unwrap_or(Vec::new()),
children,
children: children.into_iter().map(|(_start, item)| item).collect(),
},
));
}
fn include_input<'s, F, O>(
mut inner: F,
) -> impl FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, (OrgSource<'s>, O)>
where
F: FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, O>,
{
move |input: OrgSource<'_>| {
let (remaining, output) = inner(input)?;
Ok((remaining, (input, output)))
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn bullet<'s>(i: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
alt((
@@ -496,24 +519,21 @@ dolar"#,
r#"+
"#,
);
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let result = detect_plain_list(&initial_context, input);
let result = detect_plain_list(input);
assert!(result.is_ok());
}
#[test]
fn detect_eof() {
let input = OrgSource::new(r#"+"#);
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let result = detect_plain_list(&initial_context, input);
let result = detect_plain_list(input);
assert!(result.is_ok());
}
#[test]
fn detect_no_gap() {
let input = OrgSource::new(r#"+foo"#);
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let result = detect_plain_list(&initial_context, input);
let result = detect_plain_list(input);
// Since there is no whitespace after the '+' this is a paragraph, not a plain list.
assert!(result.is_err());
}
@@ -521,8 +541,7 @@ dolar"#,
#[test]
fn detect_with_gap() {
let input = OrgSource::new(r#"+ foo"#);
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let result = detect_plain_list(&initial_context, input);
let result = detect_plain_list(input);
assert!(result.is_ok());
}
}

View File

@@ -6,6 +6,7 @@ use nom::combinator::verify;
use nom::multi::many_till;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use super::Object;
use crate::error::CustomError;
@@ -104,7 +105,8 @@ pub fn radio_target<'r, 's>(
)(remaining)?;
let (remaining, _closing) = tag(">>>")(remaining)?;
let (remaining, _trailing_whitespace) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -3,13 +3,13 @@ use nom::bytes::complete::escaped;
use nom::bytes::complete::tag;
use nom::bytes::complete::take_till1;
use nom::character::complete::one_of;
use nom::character::complete::space0;
use nom::combinator::verify;
use nom::multi::many_till;
use super::org_source::OrgSource;
use super::parser_with_context::parser_with_context;
use super::util::get_consumed;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use super::Object;
use super::RegularLink;
@@ -39,7 +39,8 @@ pub fn regular_link_without_description<'r, 's>(
let (remaining, _opening_bracket) = tag("[[")(input)?;
let (remaining, _path) = pathreg(context, remaining)?;
let (remaining, _closing_bracket) = tag("]]")(remaining)?;
let (remaining, _trailing_whitespace) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,
@@ -59,7 +60,8 @@ pub fn regular_link_with_description<'r, 's>(
let (remaining, _closing_bracket) = tag("][")(remaining)?;
let (remaining, _description) = description(context, remaining)?;
let (remaining, _closing_bracket) = tag("]]")(remaining)?;
let (remaining, _trailing_whitespace) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -1,10 +1,10 @@
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::space0;
use nom::combinator::recognize;
use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::Res;
use crate::parser::parser_with_context::parser_with_context;
@@ -23,12 +23,13 @@ pub fn statistics_cookie<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn percent_statistics_cookie<'r, 's>(
_context: Context<'r, 's>,
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, StatisticsCookie<'s>> {
let (remaining, source) =
recognize(tuple((tag("["), nom::character::complete::u64, tag("%]"))))(input)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
Ok((
remaining,
StatisticsCookie {
@@ -39,7 +40,7 @@ pub fn percent_statistics_cookie<'r, 's>(
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn fraction_statistics_cookie<'r, 's>(
_context: Context<'r, 's>,
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, StatisticsCookie<'s>> {
let (remaining, source) = recognize(tuple((
@@ -49,7 +50,8 @@ pub fn fraction_statistics_cookie<'r, 's>(
nom::character::complete::u64,
tag("]"),
)))(input)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
Ok((
remaining,
StatisticsCookie {

View File

@@ -2,7 +2,6 @@ use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::one_of;
use nom::character::complete::space0;
use nom::combinator::map;
use nom::combinator::not;
use nom::combinator::opt;
@@ -13,6 +12,7 @@ use nom::multi::many_till;
use super::org_source::BracketDepth;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use super::Object;
use crate::error::CustomError;
@@ -37,7 +37,8 @@ pub fn subscript<'r, 's>(
let (remaining, _) = tag("_")(input)?;
pre(context, input)?;
let (remaining, _body) = script_body(context, remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,
@@ -56,7 +57,8 @@ pub fn superscript<'r, 's>(
let (remaining, _) = tag("^")(input)?;
pre(context, input)?;
let (remaining, _body) = script_body(context, remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
remaining,

View File

@@ -1,13 +1,13 @@
use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::one_of;
use nom::character::complete::space0;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::combinator::verify;
use nom::multi::many_till;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::CustomError;
use crate::error::MyError;
@@ -49,7 +49,8 @@ pub fn target<'r, 's>(
))));
}
let (remaining, _) = tag(">>")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((

View File

@@ -17,6 +17,7 @@ use tracing::span;
use super::org_source::OrgSource;
use super::radio_link::RematchObject;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::CustomError;
use crate::error::MyError;
@@ -204,7 +205,8 @@ fn _text_markup_object<'r, 's, 'x>(
}
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
let (remaining, _trailing_whitespace) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
Ok((remaining, children))
}
@@ -254,7 +256,8 @@ fn _text_markup_string<'r, 's, 'x>(
}
let (remaining, _close) = text_markup_end_specialized(context, remaining)?;
let (remaining, _trailing_whitespace) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
Ok((remaining, contents))
}
@@ -277,7 +280,7 @@ pub fn pre<'r, 's>(_context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSo
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn post<'r, 's>(_context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\">")), line_ending))(input)?;
let (remaining, _) = alt((recognize(one_of(" \r\n\t-.,;:!?')}[\"")), line_ending))(input)?;
Ok((remaining, ()))
}

View File

@@ -3,7 +3,6 @@ use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::digit1;
use nom::character::complete::one_of;
use nom::character::complete::space0;
use nom::character::complete::space1;
use nom::combinator::opt;
use nom::combinator::recognize;
@@ -12,6 +11,7 @@ use nom::multi::many_till;
use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::Context;
use crate::error::Res;
use crate::parser::exiting::ExitClass;
@@ -49,7 +49,8 @@ fn diary_timestamp<'r, 's>(
let (remaining, _) = tag("<%%(")(input)?;
let (remaining, _body) = sexp(context, remaining)?;
let (remaining, _) = tag(")>")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
@@ -112,7 +113,8 @@ fn active_timestamp<'r, 's>(
)))(remaining)?;
let (remaining, _) = tag(">")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
@@ -145,7 +147,8 @@ fn inactive_timestamp<'r, 's>(
)))(remaining)?;
let (remaining, _) = tag("]")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
@@ -166,7 +169,8 @@ fn active_date_range_timestamp<'r, 's>(
let (remaining, _separator) = tag("--")(remaining)?;
let (remaining, _second_timestamp) = active_timestamp(context, remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
@@ -206,7 +210,8 @@ fn active_time_range_timestamp<'r, 's>(
)))(remaining)?;
let (remaining, _) = tag(">")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
@@ -227,7 +232,8 @@ fn inactive_date_range_timestamp<'r, 's>(
let (remaining, _separator) = tag("--")(remaining)?;
let (remaining, _second_timestamp) = inactive_timestamp(context, remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((
@@ -267,7 +273,8 @@ fn inactive_time_range_timestamp<'r, 's>(
)))(remaining)?;
let (remaining, _) = tag("]")(remaining)?;
let (remaining, _) = space0(remaining)?;
let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((

View File

@@ -68,6 +68,18 @@ pub fn element_trailing_whitespace<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s
alt((eof, recognize(many0(blank_line))))(input)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn maybe_consume_object_trailing_whitespace_if_not_exiting<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
if exit_matcher_parser(context, input).is_err() {
opt(space0)(input)
} else {
Ok((input, None))
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn maybe_consume_trailing_whitespace_if_not_exiting<'r, 's>(
context: Context<'r, 's>,

View File

@@ -1 +1,2 @@
#[cfg(feature = "compare")]
include!(concat!(env!("OUT_DIR"), "/tests.rs"));