Compare commits
No commits in common. "8450785186992cd256baff77a3f1177cc54ba6d7" and "444d6758aaf6a855a2c4bb4e61a2bda7823b041c" have entirely different histories.
8450785186
...
444d6758aa
@ -1,6 +0,0 @@
|
||||
#+STARTUP: odd
|
||||
* Foo
|
||||
***** Bar
|
||||
* Baz
|
||||
*** Lorem
|
||||
* Ipsum
|
@ -2,7 +2,6 @@ use std::collections::BTreeSet;
|
||||
|
||||
use super::FileAccessInterface;
|
||||
use super::LocalFileAccessInterface;
|
||||
use crate::types::IndentationLevel;
|
||||
use crate::types::Object;
|
||||
|
||||
// TODO: Ultimately, I think we'll need most of this: https://orgmode.org/manual/In_002dbuffer-Settings.html
|
||||
@ -17,11 +16,6 @@ pub struct GlobalSettings<'g, 's> {
|
||||
///
|
||||
/// Corresponds to the org-list-allow-alphabetical elisp variable.
|
||||
pub org_list_allow_alphabetical: bool,
|
||||
|
||||
/// How many spaces a tab should be equal to.
|
||||
///
|
||||
/// Corresponds to the tab-width elisp variable.
|
||||
pub tab_width: IndentationLevel,
|
||||
}
|
||||
|
||||
impl<'g, 's> GlobalSettings<'g, 's> {
|
||||
@ -34,7 +28,6 @@ impl<'g, 's> GlobalSettings<'g, 's> {
|
||||
in_progress_todo_keywords: BTreeSet::new(),
|
||||
complete_todo_keywords: BTreeSet::new(),
|
||||
org_list_allow_alphabetical: false,
|
||||
tab_width: 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ use nom::bytes::complete::is_not;
|
||||
use nom::bytes::complete::tag;
|
||||
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::recognize;
|
||||
@ -13,6 +12,8 @@ use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::org_line_ending;
|
||||
use super::util::org_spaces0;
|
||||
use super::util::org_spaces1;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::Res;
|
||||
@ -50,7 +51,7 @@ fn fixed_width_area_line<'b, 'g, 'r, 's>(
|
||||
let (remaining, _indent) = space0(input)?;
|
||||
let (remaining, _) = tuple((
|
||||
tag(":"),
|
||||
alt((recognize(tuple((space1, is_not("\r\n")))), space0)),
|
||||
alt((recognize(tuple((org_spaces1, is_not("\r\n")))), org_spaces0)),
|
||||
org_line_ending,
|
||||
))(remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
|
@ -2,6 +2,7 @@ use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::bytes::complete::take_while;
|
||||
use nom::character::complete::digit1;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::recognize;
|
||||
@ -93,7 +94,10 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn label<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c))(input)
|
||||
alt((
|
||||
digit1,
|
||||
take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c)),
|
||||
))(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
|
@ -127,6 +127,7 @@ fn parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
}
|
||||
|
||||
fn greater_block_end<'c>(name: &'c str) -> impl ContextMatcher + 'c {
|
||||
// TODO: Can this be done without making an owned copy?
|
||||
move |context, input: OrgSource<'_>| _greater_block_end(context, input, name)
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::space0;
|
||||
use nom::character::complete::space1;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::not;
|
||||
@ -21,6 +20,8 @@ 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 crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
@ -133,27 +134,27 @@ fn headline<'b, 'g, 'r, 's>(
|
||||
))(input)?;
|
||||
|
||||
let (remaining, maybe_todo_keyword) = opt(tuple((
|
||||
space1,
|
||||
org_spaces1,
|
||||
parser_with_context!(heading_keyword)(&parser_context),
|
||||
peek(org_space_or_line_ending),
|
||||
)))(remaining)?;
|
||||
|
||||
let (remaining, maybe_priority) = opt(tuple((space1, priority_cookie)))(remaining)?;
|
||||
let (remaining, maybe_priority) = opt(tuple((org_spaces1, priority_cookie)))(remaining)?;
|
||||
|
||||
let (remaining, maybe_comment) = opt(tuple((
|
||||
space1,
|
||||
org_spaces1,
|
||||
tag("COMMENT"),
|
||||
peek(org_space_or_line_ending),
|
||||
)))(remaining)?;
|
||||
|
||||
let (remaining, maybe_title) = opt(tuple((
|
||||
space1,
|
||||
org_spaces1,
|
||||
many1(parser_with_context!(standard_set_object)(&parser_context)),
|
||||
)))(remaining)?;
|
||||
|
||||
let (remaining, maybe_tags) = opt(tuple((space0, tags)))(remaining)?;
|
||||
let (remaining, maybe_tags) = opt(tuple((org_spaces0, tags)))(remaining)?;
|
||||
|
||||
let (remaining, _) = tuple((space0, org_line_ending))(remaining)?;
|
||||
let (remaining, _) = tuple((org_spaces0, org_line_ending))(remaining)?;
|
||||
|
||||
Ok((
|
||||
remaining,
|
||||
@ -179,7 +180,11 @@ fn headline_title_end<'b, 'g, 'r, 's>(
|
||||
_context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
recognize(tuple((space0, opt(tuple((tags, space0))), org_line_ending)))(input)
|
||||
recognize(tuple((
|
||||
org_spaces0,
|
||||
opt(tuple((tags, org_spaces0))),
|
||||
org_line_ending,
|
||||
)))(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
|
@ -22,7 +22,6 @@ use super::element_parser::element;
|
||||
use super::object_parser::standard_set_object;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::include_input;
|
||||
use super::util::indentation_level;
|
||||
use super::util::non_whitespace_character;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
@ -40,7 +39,6 @@ use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::parser::util::org_space;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::CheckboxType;
|
||||
use crate::types::IndentationLevel;
|
||||
use crate::types::Object;
|
||||
use crate::types::PlainList;
|
||||
use crate::types::PlainListItem;
|
||||
@ -89,7 +87,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||
// children stores tuple of (input string, parsed object) so we can re-parse the final item
|
||||
let mut children = Vec::new();
|
||||
let mut first_item_indentation: Option<IndentationLevel> = None;
|
||||
let mut first_item_indentation: Option<usize> = None;
|
||||
let mut remaining = input;
|
||||
|
||||
// The final list item does not consume trailing blank lines (which instead get consumed by the list). We have three options here:
|
||||
@ -150,7 +148,9 @@ fn plain_list_item<'b, 'g, 'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, PlainListItem<'s>> {
|
||||
start_of_line(input)?;
|
||||
let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?;
|
||||
let (remaining, leading_whitespace) = space0(input)?;
|
||||
// It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09)
|
||||
let indent_level = leading_whitespace.len();
|
||||
let (remaining, bull) = verify(
|
||||
parser_with_context!(bullet)(context),
|
||||
|bull: &OrgSource<'_>| Into::<&str>::into(bull) != "*" || indent_level > 0,
|
||||
@ -287,7 +287,7 @@ fn plain_list_end<'b, 'g, 'r, 's>(
|
||||
)))(input)
|
||||
}
|
||||
|
||||
const fn plain_list_item_end(indent_level: IndentationLevel) -> impl ContextMatcher {
|
||||
const fn plain_list_item_end(indent_level: usize) -> impl ContextMatcher {
|
||||
let line_indented_lte_matcher = line_indented_lte(indent_level);
|
||||
move |context, input: OrgSource<'_>| {
|
||||
_plain_list_item_end(context, input, &line_indented_lte_matcher)
|
||||
@ -310,23 +310,20 @@ fn _plain_list_item_end<'b, 'g, 'r, 's>(
|
||||
)))(input)
|
||||
}
|
||||
|
||||
const fn line_indented_lte(indent_level: IndentationLevel) -> impl ContextMatcher {
|
||||
const fn line_indented_lte(indent_level: usize) -> impl ContextMatcher {
|
||||
move |context, input: OrgSource<'_>| _line_indented_lte(context, input, indent_level)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn _line_indented_lte<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
_context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
indent_level: IndentationLevel,
|
||||
indent_level: usize,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
let matched = recognize(verify(
|
||||
tuple((
|
||||
parser_with_context!(indentation_level)(context),
|
||||
non_whitespace_character,
|
||||
)),
|
||||
tuple((space0::<OrgSource<'_>, _>, non_whitespace_character)),
|
||||
// It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09)
|
||||
|((indentation_level, _leading_whitespace), _anychar)| *indentation_level <= indent_level,
|
||||
|(_space0, _anychar)| _space0.len() <= indent_level,
|
||||
))(input)?;
|
||||
|
||||
Ok(matched)
|
||||
|
@ -1,8 +1,6 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::space0;
|
||||
use nom::character::complete::space1;
|
||||
use nom::multi::many1;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
@ -10,6 +8,8 @@ use super::org_source::OrgSource;
|
||||
use super::timestamp::timestamp;
|
||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use super::util::org_line_ending;
|
||||
use super::util::org_spaces0;
|
||||
use super::util::org_spaces1;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::Res;
|
||||
@ -23,10 +23,10 @@ pub(crate) fn planning<'b, 'g, 'r, 's>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Planning<'s>> {
|
||||
start_of_line(input)?;
|
||||
let (remaining, _leading_whitespace) = space0(input)?;
|
||||
let (remaining, _leading_whitespace) = org_spaces0(input)?;
|
||||
let (remaining, _planning_parameters) =
|
||||
many1(parser_with_context!(planning_parameter)(context))(remaining)?;
|
||||
let (remaining, _trailing_ws) = tuple((space0, org_line_ending))(remaining)?;
|
||||
let (remaining, _trailing_ws) = tuple((org_spaces0, org_line_ending))(remaining)?;
|
||||
|
||||
let (remaining, _trailing_ws) =
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
@ -50,7 +50,7 @@ fn planning_parameter<'b, 'g, 'r, 's>(
|
||||
tag_no_case("SCHEDULED"),
|
||||
tag_no_case("CLOSED"),
|
||||
))(input)?;
|
||||
let (remaining, _gap) = tuple((tag(":"), space1))(remaining)?;
|
||||
let (remaining, _gap) = tuple((tag(":"), org_spaces1))(remaining)?;
|
||||
let (remaining, _timestamp) = timestamp(context, remaining)?;
|
||||
let source = get_consumed(input, remaining);
|
||||
Ok((remaining, source))
|
||||
|
@ -1,4 +1,5 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::is_a;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::none_of;
|
||||
@ -9,9 +10,11 @@ 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;
|
||||
use nom::sequence::tuple;
|
||||
use nom::Slice;
|
||||
|
||||
use super::org_source::OrgSource;
|
||||
use crate::context::parser_with_context;
|
||||
@ -20,7 +23,6 @@ use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::types::IndentationLevel;
|
||||
|
||||
pub(crate) const WORD_CONSTITUENT_CHARACTERS: &str =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
@ -241,7 +243,25 @@ pub(crate) fn org_space<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, char> {
|
||||
pub(crate) fn org_space_or_line_ending<'s>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
alt((recognize(org_space), org_line_ending))(input)
|
||||
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.
|
||||
@ -250,20 +270,3 @@ pub(crate) fn org_space_or_line_ending<'s>(
|
||||
pub(crate) fn org_line_ending<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
alt((line_ending, eof))(input)
|
||||
}
|
||||
|
||||
/// Match the whitespace at the beginning of a line and give it an indentation level.
|
||||
pub(crate) fn indentation_level<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, (IndentationLevel, OrgSource<'s>)> {
|
||||
let (remaining, leading_whitespace) = space0(input)?;
|
||||
let indentation_level = Into::<&str>::into(leading_whitespace)
|
||||
.chars()
|
||||
.map(|c| match c {
|
||||
' ' => 1,
|
||||
'\t' => context.get_global_settings().tab_width,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.sum();
|
||||
Ok((remaining, (indentation_level, leading_whitespace)))
|
||||
}
|
||||
|
@ -10,13 +10,10 @@ pub struct PlainList<'s> {
|
||||
pub children: Vec<PlainListItem<'s>>,
|
||||
}
|
||||
|
||||
/// The width that something is indented. For example, a single tab character could be a value of 4 or 8.
|
||||
pub type IndentationLevel = u16;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PlainListItem<'s> {
|
||||
pub source: &'s str,
|
||||
pub indentation: IndentationLevel,
|
||||
pub indentation: usize,
|
||||
pub bullet: &'s str,
|
||||
pub checkbox: Option<(CheckboxType, &'s str)>,
|
||||
pub tag: Vec<Object<'s>>,
|
||||
|
@ -16,7 +16,6 @@ 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;
|
||||
pub use greater_element::PlainListItem;
|
||||
|
Loading…
x
Reference in New Issue
Block a user