diff --git a/org_mode_samples/object/plain_link/all_default_links_with_search_option.org b/org_mode_samples/object/plain_link/all_default_links_with_search_option.org new file mode 100644 index 0000000..1e15e65 --- /dev/null +++ b/org_mode_samples/object/plain_link/all_default_links_with_search_option.org @@ -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 diff --git a/org_mode_samples/object/plain_link/multiple_slashes.org b/org_mode_samples/object/plain_link/multiple_slashes.org new file mode 100644 index 0000000..f8064d8 --- /dev/null +++ b/org_mode_samples/object/plain_link/multiple_slashes.org @@ -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 diff --git a/org_mode_samples/object/plain_link/with_parenthesis.org b/org_mode_samples/object/plain_link/parenthesis.org similarity index 100% rename from org_mode_samples/object/plain_link/with_parenthesis.org rename to org_mode_samples/object/plain_link/parenthesis.org diff --git a/org_mode_samples/object/plain_link/relative_path.org b/org_mode_samples/object/plain_link/relative_path.org new file mode 100644 index 0000000..addba92 --- /dev/null +++ b/org_mode_samples/object/plain_link/relative_path.org @@ -0,0 +1,4 @@ +# These do not become links +./simple.org::foo +../simple.org::foo +/simple.org::foo diff --git a/org_mode_samples/object/plain_link/search_option.org b/org_mode_samples/object/plain_link/search_option.org new file mode 100644 index 0000000..d9e1631 --- /dev/null +++ b/org_mode_samples/object/plain_link/search_option.org @@ -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 diff --git a/org_mode_samples/object/plain_link/template.org b/org_mode_samples/object/plain_link/template.org new file mode 100644 index 0000000..2a644ef --- /dev/null +++ b/org_mode_samples/object/plain_link/template.org @@ -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 diff --git a/org_mode_samples/object/regular_link/application.org b/org_mode_samples/object/regular_link/application.org new file mode 100644 index 0000000..06149c6 --- /dev/null +++ b/org_mode_samples/object/regular_link/application.org @@ -0,0 +1,2 @@ +[[file+sys://foo]] +[[file+emacs://foo]] diff --git a/org_mode_samples/object/regular_link/multiple_slashes.org b/org_mode_samples/object/regular_link/multiple_slashes.org new file mode 100644 index 0000000..bb29c8c --- /dev/null +++ b/org_mode_samples/object/regular_link/multiple_slashes.org @@ -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]] diff --git a/org_mode_samples/object/regular_link/search_option.org b/org_mode_samples/object/regular_link/search_option.org new file mode 100644 index 0000000..aba6af0 --- /dev/null +++ b/org_mode_samples/object/regular_link/search_option.org @@ -0,0 +1,2 @@ +# Does not become a search option because it is inside parenthesis. +[[https://en.wikipedia.org/wiki/Shebang_(Uni::x)]] diff --git a/src/compare/diff.rs b/src/compare/diff.rs index c08e559..04ee74c 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -2802,8 +2802,8 @@ fn compare_regular_link<'b, 's>( ), ( EmacsField::Required(":application"), - compare_identity, - compare_property_always_nil + |r| r.application.as_ref(), + compare_property_quoted_string ), ( EmacsField::Required(":search-option"), @@ -2860,7 +2860,7 @@ fn compare_radio_link<'b, 's>( ), ( EmacsField::Required(":raw-link"), - |r| Some(&r.raw_link), + |r| Some(r.get_raw_link()), compare_property_quoted_string ), ( @@ -2936,10 +2936,55 @@ fn compare_plain_link<'b, 's>( emacs: &'b Token<'s>, rust: &'b PlainLink<'s>, ) -> Result, Box> { - let this_status = DiffStatus::Good; - let message = None; + let mut this_status = DiffStatus::Good; + 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 { status: this_status, diff --git a/src/parser/plain_link.rs b/src/parser/plain_link.rs index fe97fbe..a226209 100644 --- a/src/parser/plain_link.rs +++ b/src/parser/plain_link.rs @@ -1,16 +1,25 @@ use nom::branch::alt; +use nom::bytes::complete::is_not; use nom::bytes::complete::tag; use nom::bytes::complete::tag_no_case; +use nom::bytes::complete::take; use nom::character::complete::anychar; use nom::character::complete::none_of; use nom::character::complete::one_of; +use nom::combinator::consumed; 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::opt; use nom::combinator::peek; use nom::combinator::recognize; +use nom::combinator::rest; use nom::combinator::verify; use nom::multi::many0; use nom::multi::many1; +use nom::multi::many1_count; use nom::multi::many_till; use nom::sequence::tuple; @@ -30,6 +39,7 @@ use crate::error::Res; use crate::parser::util::exit_matcher_parser; use crate::parser::util::get_consumed; use crate::parser::util::WORD_CONSTITUENT_CHARACTERS; +use crate::types::LinkType; use crate::types::PlainLink; #[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>, ) -> Res, PlainLink<'s>> { let (remaining, _) = pre(context, input)?; - let (remaining, proto) = protocol(context, remaining)?; - let (remaining, _separator) = tag(":")(remaining)?; - let (remaining, path) = path_plain(context, remaining)?; + let (remaining, path_plain) = parse_path_plain(context, remaining)?; peek(parser_with_context!(post)(context))(remaining)?; let (remaining, _trailing_whitespace) = maybe_consume_object_trailing_whitespace_if_not_exiting(context, remaining)?; @@ -49,12 +57,24 @@ pub(crate) fn plain_link<'b, 'g, 'r, 's>( remaining, PlainLink { source: source.into(), - link_type: proto.into(), - path: path.into(), + link_type: path_plain.link_type, + 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"))] fn pre<'b, 'g, 'r, 's>( _context: RefContext<'b, 'g, 'r, 's>, @@ -84,6 +104,104 @@ fn post<'b, 'g, 'r, 's>( 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, 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, Option>> { + 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, 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, 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"))] pub(crate) fn protocol<'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>, input: OrgSource<'s>, ) -> Res, OrgSource<'s>> { - let path_plain_end = path_plain_end(input.get_parenthesis_depth()); - let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode { - class: ExitClass::Gamma, - exit_matcher: &path_plain_end, - }); - let parser_context = context.with_additional_node(&parser_context); - + // The caller needs to put an instance of path_plain_end on the context before calling this. let (remaining, _components) = many1(alt(( - parser_with_context!(path_plain_no_parenthesis)(&parser_context), - parser_with_context!(path_plain_parenthesis)(&parser_context), + parser_with_context!(path_plain_no_parenthesis)(context), + parser_with_context!(path_plain_parenthesis)(context), )))(input)?; let source = get_consumed(input, remaining); Ok((remaining, source)) } -fn path_plain_end(starting_parenthesis_depth: BracketDepth) -> impl ContextMatcher { - move |context, input: OrgSource<'_>| _path_plain_end(context, input, starting_parenthesis_depth) +fn path_plain_end( + 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"))] -fn _path_plain_end<'b, 'g, 'r, 's>( +fn impl_path_plain_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>> { + 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| { !" \t\r\n[]<>()/".contains(*c) && c.is_ascii_punctuation() }))(input)?; @@ -144,7 +275,6 @@ fn _path_plain_end<'b, 'g, 'r, 's>( return disallowed_character; } - let current_depth = remaining.get_parenthesis_depth() - starting_parenthesis_depth; if current_depth == 0 { let close_parenthesis = tag::<&str, OrgSource<'_>, CustomError>>(")")(remaining); @@ -161,7 +291,6 @@ fn _path_plain_end<'b, 'g, 'r, 's>( } } - // many0 punctuation Err(nom::Err::Error(CustomError::MyError(MyError( "No path plain end".into(), )))) diff --git a/src/parser/radio_link.rs b/src/parser/radio_link.rs index 5edd8b6..5955344 100644 --- a/src/parser/radio_link.rs +++ b/src/parser/radio_link.rs @@ -41,7 +41,6 @@ pub(crate) fn radio_link<'b, 'g, 'r, 's>( source: source.into(), children: rematched_target, path: path.into(), - raw_link: path.into(), }, )); } @@ -194,8 +193,7 @@ mod tests { &Object::RadioLink(RadioLink { source: "bar ", children: vec![Object::PlainText(PlainText { source: "bar" })], - path: "bar".into(), - raw_link: "bar".into() + path: "bar".into() }) ); } @@ -238,8 +236,7 @@ mod tests { source: "*bar* ", children: vec![Object::PlainText(PlainText { source: "bar" })] })], - path: "*bar* ".into(), - raw_link: "*bar* ".into() + path: "*bar* ".into() }) ); } diff --git a/src/parser/regular_link.rs b/src/parser/regular_link.rs index e0812fe..0946ca2 100644 --- a/src/parser/regular_link.rs +++ b/src/parser/regular_link.rs @@ -4,11 +4,13 @@ use nom::branch::alt; use nom::bytes::complete::escaped; use nom::bytes::complete::is_a; use nom::bytes::complete::tag; +use nom::bytes::complete::take; use nom::bytes::complete::take_till1; use nom::bytes::complete::take_until; use nom::character::complete::anychar; use nom::combinator::consumed; use nom::combinator::eof; +use nom::combinator::flat_map; use nom::combinator::map; use nom::combinator::map_parser; use nom::combinator::opt; @@ -16,18 +18,23 @@ use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::rest; use nom::combinator::verify; +use nom::multi::many1_count; use nom::multi::many_till; use nom::sequence::tuple; use nom::InputTake; use super::object_parser::regular_link_description_set_object; +use super::org_source::BracketDepth; use super::org_source::OrgSource; +use super::plain_link::parse_file_and_application; use super::plain_link::protocol; use super::util::exit_matcher_parser; use super::util::get_consumed; 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::ContextElement; +use crate::context::ContextMatcher; use crate::context::ExitClass; use crate::context::ExitMatcherNode; use crate::context::RefContext; @@ -69,6 +76,7 @@ fn regular_link_without_description<'b, 'g, 'r, 's>( raw_link: path.raw_link, search_option: path.search_option, children: Vec::new(), + application: path.application, }, )) } @@ -95,6 +103,7 @@ fn regular_link_with_description<'b, 'g, 'r, 's>( raw_link: path.raw_link, search_option: path.search_option, children: description, + application: path.application, }, )) } @@ -105,6 +114,7 @@ struct PathReg<'s> { path: Cow<'s, str>, raw_link: Cow<'s, str>, search_option: Option>, + application: Option>, } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] @@ -126,6 +136,7 @@ fn pathreg<'b, 'g, 'r, 's>( Ok((remaining, path)) } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn parse_path_reg<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, '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) { let replaced_input = Into::>::into(replaced_link.as_str()); let (_remaining, link) = alt(( - file_path_reg, + parser_with_context!(file_path_reg)(context), id_path_reg, custom_id_path_reg, code_ref_path_reg, @@ -161,11 +172,12 @@ fn parse_path_reg<'b, 'g, 'r, 's>( path: link.path.into_owned().into(), raw_link: link.raw_link.into_owned().into(), search_option: link.search_option.map(|s| s.into_owned().into()), + application: link.application.map(|s| s.into_owned().into()), }, )) } else { alt(( - file_path_reg, + parser_with_context!(file_path_reg)(context), id_path_reg, custom_id_path_reg, code_ref_path_reg, @@ -180,6 +192,7 @@ enum ParserState { Percent, } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn apply_link_templates<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, @@ -239,14 +252,52 @@ fn apply_link_templates<'b, 'g, 'r, 's>( Some(ret) } -fn file_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s>> { - let (remaining, (raw_link, (_, path, search_option))) = consumed(tuple(( - alt((tag("file:"), peek(tag(".")), peek(tag("/")))), - recognize(many_till(anychar, alt((peek(tag("::")), eof)))), - opt(map(tuple((tag("::"), rest)), |(_, search_option)| { - search_option - })), +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] +fn file_path_reg<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, PathReg<'s>> { + 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)?; + Ok(( remaining, PathReg { @@ -256,10 +307,14 @@ fn file_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s>> { search_option: search_option .map(Into::<&str>::into) .map(Into::>::into), + application: application + .map(Into::<&str>::into) + .map(Into::>::into), }, )) } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn id_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s>> { let (remaining, (raw_link, (_, path))) = consumed(tuple((tag("id:"), rest)))(input)?; Ok(( @@ -269,10 +324,12 @@ fn id_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s>> { path: path.into(), raw_link: raw_link.into(), search_option: None, + application: None, }, )) } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn custom_id_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s>> { let (remaining, (raw_link, (_, path))) = consumed(tuple((tag("#"), rest)))(input)?; Ok(( @@ -282,10 +339,12 @@ fn custom_id_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s path: path.into(), raw_link: raw_link.into(), search_option: None, + application: None, }, )) } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn code_ref_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s>> { let (remaining, (raw_link, (_, path, _))) = consumed(tuple(( tag("("), @@ -299,10 +358,12 @@ fn code_ref_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s> path: path.into(), raw_link: raw_link.into(), search_option: None, + application: None, }, )) } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn protocol_path_reg<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, input: OrgSource<'s>, @@ -319,10 +380,12 @@ fn protocol_path_reg<'b, 'g, 'r, 's>( path: path.into(), raw_link: raw_link.into(), search_option: None, + application: None, }, )) } +#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] fn fuzzy_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s>> { let (remaining, body) = rest(input)?; Ok(( @@ -332,6 +395,7 @@ fn fuzzy_path_reg<'s>(input: OrgSource<'s>) -> Res, PathReg<'s>> { path: body.into(), raw_link: body.into(), search_option: None, + application: None, }, )) } @@ -364,3 +428,37 @@ fn description_end<'b, 'g, 'r, 's>( ) -> Res, OrgSource<'s>> { 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>> { + 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(), + )))) +} diff --git a/src/types/object.rs b/src/types/object.rs index ea884f0..16e69df 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -85,6 +85,7 @@ pub struct RegularLink<'s> { pub raw_link: Cow<'s, str>, pub search_option: Option>, pub children: Vec>, + pub application: Option>, } #[derive(Debug, PartialEq)] @@ -98,15 +99,17 @@ pub struct RadioTarget<'s> { pub struct RadioLink<'s> { pub source: &'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>, } #[derive(Debug, PartialEq)] pub struct PlainLink<'s> { pub source: &'s str, - pub link_type: &'s str, + pub link_type: LinkType<'s>, pub path: &'s str, + pub raw_link: &'s str, + pub search_option: Option<&'s str>, + pub application: Option<&'s str>, } #[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 + } +}