Handle the possibility of a title-less headline.
This commit is contained in:
parent
fc4ff97c14
commit
44e9f708c9
@ -1 +1,2 @@
|
|||||||
* DONE
|
* DONE
|
||||||
|
*
|
||||||
|
@ -546,14 +546,26 @@ fn compare_heading<'s>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Compare title
|
// Compare title
|
||||||
let title = get_property(emacs, ":title")?.ok_or("Missing :title attribute.")?;
|
let title = get_property(emacs, ":title")?;
|
||||||
let title_status = title
|
match (title, rust.title.len()) {
|
||||||
.as_list()?
|
(None, 0) => {}
|
||||||
.iter()
|
(None, _) => {
|
||||||
.zip(rust.title.iter())
|
this_status = DiffStatus::Bad;
|
||||||
.map(|(emacs_child, rust_child)| compare_object(source, emacs_child, rust_child))
|
message = Some(format!(
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
"Titles do not match (emacs != rust): {:?} != {:?}",
|
||||||
child_status.push(artificial_diff_scope("title".to_owned(), title_status)?);
|
title, rust.title
|
||||||
|
))
|
||||||
|
}
|
||||||
|
(Some(title), _) => {
|
||||||
|
let title_status = title
|
||||||
|
.as_list()?
|
||||||
|
.iter()
|
||||||
|
.zip(rust.title.iter())
|
||||||
|
.map(|(emacs_child, rust_child)| compare_object(source, emacs_child, rust_child))
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
child_status.push(artificial_diff_scope("title".to_owned(), title_status)?);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Compare priority
|
// Compare priority
|
||||||
let priority = get_property(emacs, ":priority")?;
|
let priority = get_property(emacs, ":priority")?;
|
||||||
|
@ -8,6 +8,7 @@ use nom::combinator::eof;
|
|||||||
use nom::combinator::map;
|
use nom::combinator::map;
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
@ -19,6 +20,11 @@ use nom::sequence::tuple;
|
|||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::section::section;
|
use super::section::section;
|
||||||
use super::util::get_consumed;
|
use super::util::get_consumed;
|
||||||
|
use super::util::org_line_ending;
|
||||||
|
use super::util::org_space;
|
||||||
|
use super::util::org_space_or_line_ending;
|
||||||
|
use super::util::org_spaces0;
|
||||||
|
use super::util::org_spaces1;
|
||||||
use super::util::start_of_line;
|
use super::util::start_of_line;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::ContextElement;
|
use crate::context::ContextElement;
|
||||||
@ -81,10 +87,10 @@ fn _heading<'b, 'g, 'r, 's>(
|
|||||||
Heading {
|
Heading {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
stars: star_count,
|
stars: star_count,
|
||||||
todo_keyword: maybe_todo_keyword.map(|((todo_keyword_type, todo_keyword), _ws)| {
|
todo_keyword: maybe_todo_keyword.map(|(todo_keyword_type, todo_keyword)| {
|
||||||
(todo_keyword_type, Into::<&str>::into(todo_keyword))
|
(todo_keyword_type, Into::<&str>::into(todo_keyword))
|
||||||
}),
|
}),
|
||||||
priority_cookie: maybe_priority.map(|(priority, _)| priority),
|
priority_cookie: maybe_priority.map(|(_, priority)| priority),
|
||||||
title,
|
title,
|
||||||
tags: heading_tags,
|
tags: heading_tags,
|
||||||
children,
|
children,
|
||||||
@ -109,9 +115,9 @@ fn headline<'b, 'g, 'r, 's>(
|
|||||||
OrgSource<'s>,
|
OrgSource<'s>,
|
||||||
(
|
(
|
||||||
usize,
|
usize,
|
||||||
Option<((TodoKeywordType, OrgSource<'s>), OrgSource<'s>)>,
|
Option<(TodoKeywordType, OrgSource<'s>)>,
|
||||||
Option<(PriorityCookie, OrgSource<'s>)>,
|
Option<(OrgSource<'s>, PriorityCookie)>,
|
||||||
Option<(OrgSource<'s>, OrgSource<'s>)>,
|
Option<OrgSource<'s>>,
|
||||||
Vec<Object<'s>>,
|
Vec<Object<'s>>,
|
||||||
Vec<&'s str>,
|
Vec<&'s str>,
|
||||||
),
|
),
|
||||||
@ -122,45 +128,45 @@ fn headline<'b, 'g, 'r, 's>(
|
|||||||
});
|
});
|
||||||
let parser_context = context.with_additional_node(&parser_context);
|
let parser_context = context.with_additional_node(&parser_context);
|
||||||
|
|
||||||
let (
|
let (remaining, (_, star_count, _)) = tuple((
|
||||||
remaining,
|
|
||||||
(
|
|
||||||
_,
|
|
||||||
star_count,
|
|
||||||
_,
|
|
||||||
maybe_todo_keyword,
|
|
||||||
maybe_priority,
|
|
||||||
maybe_comment,
|
|
||||||
title,
|
|
||||||
maybe_tags,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
),
|
|
||||||
) = tuple((
|
|
||||||
start_of_line,
|
start_of_line,
|
||||||
verify(many1_count(tag("*")), |star_count| {
|
verify(many1_count(tag("*")), |star_count| {
|
||||||
*star_count > parent_stars
|
*star_count > parent_stars
|
||||||
}),
|
}),
|
||||||
space1,
|
peek(org_space),
|
||||||
opt(tuple((
|
|
||||||
parser_with_context!(heading_keyword)(&parser_context),
|
|
||||||
space1,
|
|
||||||
))),
|
|
||||||
opt(tuple((priority_cookie, space1))),
|
|
||||||
opt(tuple((tag("COMMENT"), space1))),
|
|
||||||
many1(parser_with_context!(standard_set_object)(&parser_context)),
|
|
||||||
opt(tuple((space0, tags))),
|
|
||||||
space0,
|
|
||||||
alt((line_ending, eof)),
|
|
||||||
))(input)?;
|
))(input)?;
|
||||||
|
|
||||||
|
let (remaining, maybe_todo_keyword) = opt(tuple((
|
||||||
|
org_spaces1,
|
||||||
|
parser_with_context!(heading_keyword)(&parser_context),
|
||||||
|
peek(org_space_or_line_ending),
|
||||||
|
)))(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, maybe_priority) = opt(tuple((org_spaces1, priority_cookie)))(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, maybe_comment) = opt(tuple((
|
||||||
|
org_spaces1,
|
||||||
|
tag("COMMENT"),
|
||||||
|
peek(org_space_or_line_ending),
|
||||||
|
)))(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, maybe_title) = opt(tuple((
|
||||||
|
org_spaces1,
|
||||||
|
many1(parser_with_context!(standard_set_object)(&parser_context)),
|
||||||
|
)))(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, maybe_tags) = opt(tuple((org_spaces0, tags)))(remaining)?;
|
||||||
|
|
||||||
|
let (remaining, _) = tuple((org_spaces0, org_line_ending))(remaining)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
(
|
(
|
||||||
star_count,
|
star_count,
|
||||||
maybe_todo_keyword,
|
maybe_todo_keyword.map(|(_, todo, _)| todo),
|
||||||
maybe_priority,
|
maybe_priority,
|
||||||
maybe_comment,
|
maybe_comment.map(|(_, comment, _)| comment),
|
||||||
title,
|
maybe_title.map(|(_, title)| title).unwrap_or(Vec::new()),
|
||||||
maybe_tags
|
maybe_tags
|
||||||
.map(|(_ws, tags)| {
|
.map(|(_ws, tags)| {
|
||||||
tags.into_iter()
|
tags.into_iter()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
|
use nom::bytes::complete::is_a;
|
||||||
use nom::character::complete::anychar;
|
use nom::character::complete::anychar;
|
||||||
use nom::character::complete::line_ending;
|
use nom::character::complete::line_ending;
|
||||||
use nom::character::complete::none_of;
|
use nom::character::complete::none_of;
|
||||||
@ -9,9 +10,11 @@ use nom::combinator::not;
|
|||||||
use nom::combinator::opt;
|
use nom::combinator::opt;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
|
use nom::combinator::verify;
|
||||||
use nom::multi::many0;
|
use nom::multi::many0;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
|
use nom::Slice;
|
||||||
|
|
||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::parser_with_context;
|
||||||
@ -212,6 +215,9 @@ fn text_until_eol<'r, 's>(
|
|||||||
Ok(line.trim())
|
Ok(line.trim())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a tuple of (input, output) from a nom parser.
|
||||||
|
///
|
||||||
|
/// This is similar to recognize except it returns the input instead of the portion of the input that was consumed.
|
||||||
pub(crate) fn include_input<'s, F, O>(
|
pub(crate) fn include_input<'s, F, O>(
|
||||||
mut inner: F,
|
mut inner: F,
|
||||||
) -> impl FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, (OrgSource<'s>, O)>
|
) -> impl FnMut(OrgSource<'s>) -> Res<OrgSource<'s>, (OrgSource<'s>, O)>
|
||||||
@ -223,3 +229,44 @@ where
|
|||||||
Ok((remaining, (input, output)))
|
Ok((remaining, (input, output)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Match single space or tab.
|
||||||
|
///
|
||||||
|
/// In org-mode syntax, spaces and tabs are interchangeable.
|
||||||
|
pub(crate) fn org_space<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, char> {
|
||||||
|
one_of(" \t")(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Matches a single space, tab, line ending, or end of file.
|
||||||
|
///
|
||||||
|
/// In org-mode syntax there are often delimiters that could be any whitespace at all or the end of file.
|
||||||
|
pub(crate) fn org_space_or_line_ending<'s>(
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
alt((recognize(one_of(" \t")), org_line_ending))(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Match as many spaces and tabs as possible. No minimum match.
|
||||||
|
///
|
||||||
|
/// In org-mode syntax, spaces and tabs are interchangeable.
|
||||||
|
pub(crate) fn org_spaces0<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
let found = is_a(" \t")(input);
|
||||||
|
if found.is_ok() {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
Ok((input, input.slice(..0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Match as many spaces and tabs as possible. Minimum 1 character.
|
||||||
|
///
|
||||||
|
/// In org-mode syntax, spaces and tabs are interchangeable.
|
||||||
|
pub(crate) fn org_spaces1<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
verify(is_a(" \t"), |res: &OrgSource<'_>| res.len() > 0)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Match a line break or the end of the file.
|
||||||
|
///
|
||||||
|
/// In org-mode syntax, the end of the file can serve the same purpose as a line break syntactically.
|
||||||
|
pub(crate) fn org_line_ending<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
|
alt((line_ending, eof))(input)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user