Merge branch 'plain_link_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

This commit is contained in:
Tom Alexander 2023-10-08 10:04:44 -04:00
commit 488372b070
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
14 changed files with 409 additions and 41 deletions

View File

@ -0,0 +1,25 @@
non-link text::foo
eww://foo::foo
rmail://foo::foo
mhe://foo::foo
irc://foo::foo
info://foo::foo
gnus://foo::foo
docview://foo::foo
bibtex://foo::foo
bbdb://foo::foo
w3m://foo::foo
doi://foo::foo
file+sys://foo::foo
file+emacs://foo::foo
shell://foo::foo
news://foo::foo
mailto://foo::foo
https://foo::foo
http://foo::foo
ftp://foo::foo
help://foo::foo
file://foo::foo
elisp://foo::foo
randomfakeprotocl://foo::foo
non-link text::foo

View File

@ -0,0 +1,13 @@
file:foo
file:/bar
file://baz
file:///lorem
file:////ipsum
file://///dolar
https:foo
https:/bar
https://baz
https:///lorem
https:////ipsum
https://///dolar

View File

@ -0,0 +1,4 @@
# These do not become links
./simple.org::foo
../simple.org::foo
/simple.org::foo

View File

@ -0,0 +1,16 @@
file:simple.org::foo
file:simple.org::#foo
file:simple.org::foo bar
file:simple.org::foo
bar
file:simple.org::foo::bar
file:simple.org::/foo/
# Does not become a search option because it is inside parenthesis.
https://en.wikipedia.org/wiki/Shebang_(Uni::x)
file:simple.org::* foo
file:simple.org::*bar
file:simple.org::b*az

View File

@ -0,0 +1,8 @@
# None of these end up as links.
#+LINK: foo https://foo.bar/baz#%s
foo::lorem
cat::bat
#+LINK: cat https://dog%s
cat:bat

View File

@ -0,0 +1,2 @@
[[file+sys://foo]]
[[file+emacs://foo]]

View File

@ -0,0 +1,20 @@
[[file:foo]]
[[file:/bar]]
[[file://baz]]
[[file:///lorem]]
[[file:////ipsum]]
[[file://///dolar]]
[[foo]]
[[/bar]]
[[//baz]]
[[///lorem]]
[[////ipsum]]
[[/////dolar]]
[[https:foo]]
[[https:/bar]]
[[https://baz]]
[[https:///lorem]]
[[https:////ipsum]]
[[https://///dolar]]

View File

@ -0,0 +1,2 @@
# Does not become a search option because it is inside parenthesis.
[[https://en.wikipedia.org/wiki/Shebang_(Uni::x)]]

View File

@ -2802,8 +2802,8 @@ fn compare_regular_link<'b, 's>(
), ),
( (
EmacsField::Required(":application"), EmacsField::Required(":application"),
compare_identity, |r| r.application.as_ref(),
compare_property_always_nil compare_property_quoted_string
), ),
( (
EmacsField::Required(":search-option"), EmacsField::Required(":search-option"),
@ -2860,7 +2860,7 @@ fn compare_radio_link<'b, 's>(
), ),
( (
EmacsField::Required(":raw-link"), EmacsField::Required(":raw-link"),
|r| Some(&r.raw_link), |r| Some(r.get_raw_link()),
compare_property_quoted_string compare_property_quoted_string
), ),
( (
@ -2936,10 +2936,55 @@ fn compare_plain_link<'b, 's>(
emacs: &'b Token<'s>, emacs: &'b Token<'s>,
rust: &'b PlainLink<'s>, rust: &'b PlainLink<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let message = None; let mut message = None;
// TODO: Compare :type :path :format :raw-link :application :search-option if let Some((new_status, new_message)) = compare_properties!(
emacs,
rust,
(
EmacsField::Required(":type"),
|r| {
match &r.link_type {
LinkType::File => Some(Cow::Borrowed("file")),
LinkType::Protocol(protocol) => Some(protocol.clone()),
LinkType::Id => Some(Cow::Borrowed("id")),
LinkType::CustomId => Some(Cow::Borrowed("custom-id")),
LinkType::CodeRef => Some(Cow::Borrowed("coderef")),
LinkType::Fuzzy => Some(Cow::Borrowed("fuzzy")),
}
},
compare_property_quoted_string
),
(
EmacsField::Required(":path"),
|r| Some(r.path),
compare_property_quoted_string
),
(
EmacsField::Required(":format"),
|_| Some("plain"),
compare_property_unquoted_atom
),
(
EmacsField::Required(":raw-link"),
|r| Some(r.raw_link),
compare_property_quoted_string
),
(
EmacsField::Required(":application"),
|r| r.application,
compare_property_quoted_string
),
(
EmacsField::Required(":search-option"),
|r| r.search_option,
compare_property_quoted_string
)
)? {
this_status = new_status;
message = new_message;
}
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,

View File

@ -1,16 +1,25 @@
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case; use nom::bytes::complete::tag_no_case;
use nom::bytes::complete::take;
use nom::character::complete::anychar; use nom::character::complete::anychar;
use nom::character::complete::none_of; use nom::character::complete::none_of;
use nom::character::complete::one_of; use nom::character::complete::one_of;
use nom::combinator::consumed;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::flat_map;
use nom::combinator::map;
use nom::combinator::map_parser;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::rest;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many0; use nom::multi::many0;
use nom::multi::many1; use nom::multi::many1;
use nom::multi::many1_count;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
@ -30,6 +39,7 @@ use crate::error::Res;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS; use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
use crate::types::LinkType;
use crate::types::PlainLink; use crate::types::PlainLink;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@ -38,9 +48,7 @@ pub(crate) fn plain_link<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PlainLink<'s>> { ) -> Res<OrgSource<'s>, PlainLink<'s>> {
let (remaining, _) = pre(context, input)?; let (remaining, _) = pre(context, input)?;
let (remaining, proto) = protocol(context, remaining)?; let (remaining, path_plain) = parse_path_plain(context, remaining)?;
let (remaining, _separator) = tag(":")(remaining)?;
let (remaining, path) = path_plain(context, remaining)?;
peek(parser_with_context!(post)(context))(remaining)?; peek(parser_with_context!(post)(context))(remaining)?;
let (remaining, _trailing_whitespace) = let (remaining, _trailing_whitespace) =
maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?;
@ -49,12 +57,24 @@ pub(crate) fn plain_link<'b, 'g, 'r, 's>(
remaining, remaining,
PlainLink { PlainLink {
source: source.into(), source: source.into(),
link_type: proto.into(), link_type: path_plain.link_type,
path: path.into(), path: path_plain.path,
raw_link: path_plain.raw_link,
search_option: path_plain.search_option,
application: path_plain.application,
}, },
)) ))
} }
#[derive(Debug)]
struct PathPlain<'s> {
link_type: LinkType<'s>,
path: &'s str,
raw_link: &'s str,
search_option: Option<&'s str>,
application: Option<&'s str>,
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn pre<'b, 'g, 'r, 's>( fn pre<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>, _context: RefContext<'b, 'g, 'r, 's>,
@ -84,6 +104,104 @@ fn post<'b, 'g, 'r, 's>(
Ok((remaining, ())) Ok((remaining, ()))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parse_path_plain<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PathPlain<'s>> {
alt((
parser_with_context!(file_path_plain)(context),
parser_with_context!(protocol_path_plain)(context),
))(input)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn parse_file_and_application<'s>(
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Option<OrgSource<'s>>> {
let (remaining, _) = tag("file")(input)?;
let (remaining, application) =
opt(map(tuple((tag("+"), rest)), |(_, application)| application))(remaining)?;
// Assert we consumed the entire protocol.
not(anychar)(remaining)?;
Ok((remaining, application))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn file_path_plain<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PathPlain<'s>> {
let path_plain_end = path_plain_end(input.get_parenthesis_depth(), true);
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &path_plain_end,
});
let parser_context = context.with_additional_node(&parser_context);
let (remaining, (raw_link, (_, application, _, _, path, search_option))) = consumed(tuple((
peek(tag("file")),
map_parser(
parser_with_context!(protocol)(&parser_context),
parse_file_and_application,
),
tag(":"),
opt(flat_map(
peek(map(verify(many1_count(tag("/")), |c| *c >= 3), |c| c - 1)),
take,
)),
parser_with_context!(path_plain)(&parser_context),
opt(map(
tuple((
tag("::"),
verify(is_not(" \t\r\n"), |search_option| {
Into::<&str>::into(search_option)
.chars()
.any(char::is_alphanumeric)
}),
)),
|(_, search_option)| search_option,
)),
)))(input)?;
Ok((
remaining,
PathPlain {
link_type: LinkType::File,
path: path.into(),
raw_link: raw_link.into(),
search_option: search_option.map(Into::<&str>::into),
application: application.map(Into::<&str>::into),
},
))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn protocol_path_plain<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PathPlain<'s>> {
let path_plain_end = path_plain_end(input.get_parenthesis_depth(), false);
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &path_plain_end,
});
let parser_context = context.with_additional_node(&parser_context);
let (remaining, (raw_link, (protocol, _, path))) = consumed(tuple((
parser_with_context!(protocol)(&parser_context),
tag(":"),
parser_with_context!(path_plain)(&parser_context),
)))(input)?;
Ok((
remaining,
PathPlain {
link_type: LinkType::Protocol(protocol.into()),
path: path.into(),
raw_link: raw_link.into(),
search_option: None,
application: None,
},
))
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn protocol<'b, 'g, 'r, 's>( pub(crate) fn protocol<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
@ -109,32 +227,45 @@ fn path_plain<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let path_plain_end = path_plain_end(input.get_parenthesis_depth()); // The caller needs to put an instance of path_plain_end on the context before calling this.
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &path_plain_end,
});
let parser_context = context.with_additional_node(&parser_context);
let (remaining, _components) = many1(alt(( let (remaining, _components) = many1(alt((
parser_with_context!(path_plain_no_parenthesis)(&parser_context), parser_with_context!(path_plain_no_parenthesis)(context),
parser_with_context!(path_plain_parenthesis)(&parser_context), parser_with_context!(path_plain_parenthesis)(context),
)))(input)?; )))(input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, source)) Ok((remaining, source))
} }
fn path_plain_end(starting_parenthesis_depth: BracketDepth) -> impl ContextMatcher { fn path_plain_end(
move |context, input: OrgSource<'_>| _path_plain_end(context, input, starting_parenthesis_depth) starting_parenthesis_depth: BracketDepth,
enable_search_option: bool,
) -> impl ContextMatcher {
move |context, input: OrgSource<'_>| {
impl_path_plain_end(
context,
input,
starting_parenthesis_depth,
enable_search_option,
)
}
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _path_plain_end<'b, 'g, 'r, 's>( fn impl_path_plain_end<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
starting_parenthesis_depth: BracketDepth, starting_parenthesis_depth: BracketDepth,
enable_search_option: bool,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth;
if enable_search_option && current_depth == 0 {
let search_option = peek(tag("::"))(input);
if search_option.is_ok() {
return search_option;
}
}
let (remaining, _leading_punctuation) = many0(verify(anychar, |c| { let (remaining, _leading_punctuation) = many0(verify(anychar, |c| {
!" \t\r\n[]<>()/".contains(*c) && c.is_ascii_punctuation() !" \t\r\n[]<>()/".contains(*c) && c.is_ascii_punctuation()
}))(input)?; }))(input)?;
@ -144,7 +275,6 @@ fn _path_plain_end<'b, 'g, 'r, 's>(
return disallowed_character; return disallowed_character;
} }
let current_depth = remaining.get_parenthesis_depth() - starting_parenthesis_depth;
if current_depth == 0 { if current_depth == 0 {
let close_parenthesis = let close_parenthesis =
tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(remaining); tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(remaining);
@ -161,7 +291,6 @@ fn _path_plain_end<'b, 'g, 'r, 's>(
} }
} }
// many0 punctuation
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::MyError(MyError(
"No path plain end".into(), "No path plain end".into(),
)))) ))))

View File

@ -41,7 +41,6 @@ pub(crate) fn radio_link<'b, 'g, 'r, 's>(
source: source.into(), source: source.into(),
children: rematched_target, children: rematched_target,
path: path.into(), path: path.into(),
raw_link: path.into(),
}, },
)); ));
} }
@ -194,8 +193,7 @@ mod tests {
&Object::RadioLink(RadioLink { &Object::RadioLink(RadioLink {
source: "bar ", source: "bar ",
children: vec![Object::PlainText(PlainText { source: "bar" })], children: vec![Object::PlainText(PlainText { source: "bar" })],
path: "bar".into(), path: "bar".into()
raw_link: "bar".into()
}) })
); );
} }
@ -238,8 +236,7 @@ mod tests {
source: "*bar* ", source: "*bar* ",
children: vec![Object::PlainText(PlainText { source: "bar" })] children: vec![Object::PlainText(PlainText { source: "bar" })]
})], })],
path: "*bar* ".into(), path: "*bar* ".into()
raw_link: "*bar* ".into()
}) })
); );
} }

View File

@ -4,11 +4,13 @@ use nom::branch::alt;
use nom::bytes::complete::escaped; use nom::bytes::complete::escaped;
use nom::bytes::complete::is_a; use nom::bytes::complete::is_a;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::bytes::complete::take;
use nom::bytes::complete::take_till1; use nom::bytes::complete::take_till1;
use nom::bytes::complete::take_until; use nom::bytes::complete::take_until;
use nom::character::complete::anychar; use nom::character::complete::anychar;
use nom::combinator::consumed; use nom::combinator::consumed;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::flat_map;
use nom::combinator::map; use nom::combinator::map;
use nom::combinator::map_parser; use nom::combinator::map_parser;
use nom::combinator::opt; use nom::combinator::opt;
@ -16,18 +18,23 @@ use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::rest; use nom::combinator::rest;
use nom::combinator::verify; use nom::combinator::verify;
use nom::multi::many1_count;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use nom::InputTake; use nom::InputTake;
use super::object_parser::regular_link_description_set_object; use super::object_parser::regular_link_description_set_object;
use super::org_source::BracketDepth;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::plain_link::parse_file_and_application;
use super::plain_link::protocol; use super::plain_link::protocol;
use super::util::exit_matcher_parser; use super::util::exit_matcher_parser;
use super::util::get_consumed; use super::util::get_consumed;
use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting; use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use super::util::text_until_exit;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::ContextMatcher;
use crate::context::ExitClass; use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
@ -69,6 +76,7 @@ fn regular_link_without_description<'b, 'g, 'r, 's>(
raw_link: path.raw_link, raw_link: path.raw_link,
search_option: path.search_option, search_option: path.search_option,
children: Vec::new(), children: Vec::new(),
application: path.application,
}, },
)) ))
} }
@ -95,6 +103,7 @@ fn regular_link_with_description<'b, 'g, 'r, 's>(
raw_link: path.raw_link, raw_link: path.raw_link,
search_option: path.search_option, search_option: path.search_option,
children: description, children: description,
application: path.application,
}, },
)) ))
} }
@ -105,6 +114,7 @@ struct PathReg<'s> {
path: Cow<'s, str>, path: Cow<'s, str>,
raw_link: Cow<'s, str>, raw_link: Cow<'s, str>,
search_option: Option<Cow<'s, str>>, search_option: Option<Cow<'s, str>>,
application: Option<Cow<'s, str>>,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@ -126,6 +136,7 @@ fn pathreg<'b, 'g, 'r, 's>(
Ok((remaining, path)) Ok((remaining, path))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parse_path_reg<'b, 'g, 'r, 's>( fn parse_path_reg<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@ -133,7 +144,7 @@ fn parse_path_reg<'b, 'g, 'r, 's>(
if let Some(replaced_link) = apply_link_templates(context, input) { if let Some(replaced_link) = apply_link_templates(context, input) {
let replaced_input = Into::<OrgSource<'_>>::into(replaced_link.as_str()); let replaced_input = Into::<OrgSource<'_>>::into(replaced_link.as_str());
let (_remaining, link) = alt(( let (_remaining, link) = alt((
file_path_reg, parser_with_context!(file_path_reg)(context),
id_path_reg, id_path_reg,
custom_id_path_reg, custom_id_path_reg,
code_ref_path_reg, code_ref_path_reg,
@ -161,11 +172,12 @@ fn parse_path_reg<'b, 'g, 'r, 's>(
path: link.path.into_owned().into(), path: link.path.into_owned().into(),
raw_link: link.raw_link.into_owned().into(), raw_link: link.raw_link.into_owned().into(),
search_option: link.search_option.map(|s| s.into_owned().into()), search_option: link.search_option.map(|s| s.into_owned().into()),
application: link.application.map(|s| s.into_owned().into()),
}, },
)) ))
} else { } else {
alt(( alt((
file_path_reg, parser_with_context!(file_path_reg)(context),
id_path_reg, id_path_reg,
custom_id_path_reg, custom_id_path_reg,
code_ref_path_reg, code_ref_path_reg,
@ -180,6 +192,7 @@ enum ParserState {
Percent, Percent,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn apply_link_templates<'b, 'g, 'r, 's>( fn apply_link_templates<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@ -239,14 +252,52 @@ fn apply_link_templates<'b, 'g, 'r, 's>(
Some(ret) Some(ret)
} }
fn file_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> { #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
let (remaining, (raw_link, (_, path, search_option))) = consumed(tuple(( fn file_path_reg<'b, 'g, 'r, 's>(
alt((tag("file:"), peek(tag(".")), peek(tag("/")))), context: RefContext<'b, 'g, 'r, 's>,
recognize(many_till(anychar, alt((peek(tag("::")), eof)))), input: OrgSource<'s>,
opt(map(tuple((tag("::"), rest)), |(_, search_option)| { ) -> Res<OrgSource<'s>, PathReg<'s>> {
search_option let path_reg_end = path_reg_end(input.get_parenthesis_depth(), true);
})), let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Gamma,
exit_matcher: &path_reg_end,
});
let parser_context = context.with_additional_node(&parser_context);
let (remaining, (raw_link, (application, _, path, search_option))) = consumed(tuple((
alt((
map(
tuple((
peek(tag("file")),
map_parser(
parser_with_context!(protocol)(&parser_context),
parse_file_and_application,
),
tag(":"),
)),
|(_, application, _)| application,
),
map(peek(tag(".")), |_| None),
map(peek(tag("/")), |_| None),
)),
opt(flat_map(
peek(map(verify(many1_count(tag("/")), |c| *c >= 3), |c| c - 1)),
take,
)),
parser_with_context!(text_until_exit)(&parser_context),
opt(map(
tuple((
tag("::"),
verify(rest, |search_option| {
Into::<&str>::into(search_option)
.chars()
.any(char::is_alphanumeric)
}),
)),
|(_, search_option)| search_option,
)),
)))(input)?; )))(input)?;
Ok(( Ok((
remaining, remaining,
PathReg { PathReg {
@ -256,10 +307,14 @@ fn file_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
search_option: search_option search_option: search_option
.map(Into::<&str>::into) .map(Into::<&str>::into)
.map(Into::<Cow<str>>::into), .map(Into::<Cow<str>>::into),
application: application
.map(Into::<&str>::into)
.map(Into::<Cow<str>>::into),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn id_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> { fn id_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
let (remaining, (raw_link, (_, path))) = consumed(tuple((tag("id:"), rest)))(input)?; let (remaining, (raw_link, (_, path))) = consumed(tuple((tag("id:"), rest)))(input)?;
Ok(( Ok((
@ -269,10 +324,12 @@ fn id_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
path: path.into(), path: path.into(),
raw_link: raw_link.into(), raw_link: raw_link.into(),
search_option: None, search_option: None,
application: None,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn custom_id_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> { fn custom_id_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
let (remaining, (raw_link, (_, path))) = consumed(tuple((tag("#"), rest)))(input)?; let (remaining, (raw_link, (_, path))) = consumed(tuple((tag("#"), rest)))(input)?;
Ok(( Ok((
@ -282,10 +339,12 @@ fn custom_id_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s
path: path.into(), path: path.into(),
raw_link: raw_link.into(), raw_link: raw_link.into(),
search_option: None, search_option: None,
application: None,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn code_ref_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> { fn code_ref_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
let (remaining, (raw_link, (_, path, _))) = consumed(tuple(( let (remaining, (raw_link, (_, path, _))) = consumed(tuple((
tag("("), tag("("),
@ -299,10 +358,12 @@ fn code_ref_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>
path: path.into(), path: path.into(),
raw_link: raw_link.into(), raw_link: raw_link.into(),
search_option: None, search_option: None,
application: None,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn protocol_path_reg<'b, 'g, 'r, 's>( fn protocol_path_reg<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
@ -319,10 +380,12 @@ fn protocol_path_reg<'b, 'g, 'r, 's>(
path: path.into(), path: path.into(),
raw_link: raw_link.into(), raw_link: raw_link.into(),
search_option: None, search_option: None,
application: None,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn fuzzy_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> { fn fuzzy_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
let (remaining, body) = rest(input)?; let (remaining, body) = rest(input)?;
Ok(( Ok((
@ -332,6 +395,7 @@ fn fuzzy_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
path: body.into(), path: body.into(),
raw_link: body.into(), raw_link: body.into(),
search_option: None, search_option: None,
application: None,
}, },
)) ))
} }
@ -364,3 +428,37 @@ fn description_end<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
tag("]]")(input) tag("]]")(input)
} }
fn path_reg_end(
starting_parenthesis_depth: BracketDepth,
enable_search_option: bool,
) -> impl ContextMatcher {
move |context, input: OrgSource<'_>| {
impl_path_reg_end(
context,
input,
starting_parenthesis_depth,
enable_search_option,
)
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn impl_path_reg_end<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
starting_parenthesis_depth: BracketDepth,
enable_search_option: bool,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth;
if enable_search_option && current_depth == 0 {
let search_option = peek(tag("::"))(input);
if search_option.is_ok() {
return search_option;
}
}
Err(nom::Err::Error(CustomError::MyError(MyError(
"No path reg end".into(),
))))
}

View File

@ -85,6 +85,7 @@ pub struct RegularLink<'s> {
pub raw_link: Cow<'s, str>, pub raw_link: Cow<'s, str>,
pub search_option: Option<Cow<'s, str>>, pub search_option: Option<Cow<'s, str>>,
pub children: Vec<Object<'s>>, pub children: Vec<Object<'s>>,
pub application: Option<Cow<'s, str>>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -98,15 +99,17 @@ pub struct RadioTarget<'s> {
pub struct RadioLink<'s> { pub struct RadioLink<'s> {
pub source: &'s str, pub source: &'s str,
pub path: &'s str, pub path: &'s str,
pub raw_link: &'s str, // TODO: Is this always the same as path? If so, this could be a function instead of a field.
pub children: Vec<Object<'s>>, pub children: Vec<Object<'s>>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct PlainLink<'s> { pub struct PlainLink<'s> {
pub source: &'s str, pub source: &'s str,
pub link_type: &'s str, pub link_type: LinkType<'s>,
pub path: &'s str, pub path: &'s str,
pub raw_link: &'s str,
pub search_option: Option<&'s str>,
pub application: Option<&'s str>,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -720,3 +723,9 @@ impl<'s> RegularLink<'s> {
}) })
} }
} }
impl<'s> RadioLink<'s> {
pub fn get_raw_link(&self) -> &'s str {
self.path
}
}