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 00000000..1e15e652 --- /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/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/search_option.org b/org_mode_samples/object/plain_link/search_option.org index 489153bb..b2e0853e 100644 --- a/org_mode_samples/object/plain_link/search_option.org +++ b/org_mode_samples/object/plain_link/search_option.org @@ -6,3 +6,6 @@ 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) diff --git a/src/compare/diff.rs b/src/compare/diff.rs index 8fd64b3a..16902dd7 100644 --- a/src/compare/diff.rs +++ b/src/compare/diff.rs @@ -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"), + compare_identity, + compare_property_always_nil + ), + ( + 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 fe97fbe3..ac205dce 100644 --- a/src/parser/plain_link.rs +++ b/src/parser/plain_link.rs @@ -1,11 +1,15 @@ use nom::branch::alt; +use nom::bytes::complete::is_not; use nom::bytes::complete::tag; use nom::bytes::complete::tag_no_case; 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::map; use nom::combinator::not; +use nom::combinator::opt; use nom::combinator::peek; use nom::combinator::recognize; use nom::combinator::verify; @@ -30,6 +34,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 +43,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 +52,22 @@ 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, }, )) } +#[derive(Debug)] +struct PathPlain<'s> { + link_type: LinkType<'s>, + path: &'s str, + raw_link: &'s str, + search_option: 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 +97,59 @@ fn post<'b, 'g, 'r, 's>( Ok((remaining, ())) } +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) +} + +fn file_path_plain<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, PathPlain<'s>> { + let (remaining, (raw_link, (_, path, search_option))) = consumed(tuple(( + tag("file:"), + parser_with_context!(path_plain)(context), + opt(map( + tuple((tag("::"), is_not(" \t\r\n"))), + |(_, 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), + }, + )) +} + +fn protocol_path_plain<'b, 'g, 'r, 's>( + context: RefContext<'b, 'g, 'r, 's>, + input: OrgSource<'s>, +) -> Res, PathPlain<'s>> { + let (remaining, (raw_link, (protocol, _, path))) = consumed(tuple(( + parser_with_context!(protocol)(context), + tag(":"), + parser_with_context!(path_plain)(context), + )))(input)?; + Ok(( + remaining, + PathPlain { + link_type: LinkType::Protocol(protocol.into()), + path: path.into(), + raw_link: raw_link.into(), + search_option: None, + }, + )) +} + #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] pub(crate) fn protocol<'b, 'g, 'r, 's>( context: RefContext<'b, 'g, 'r, 's>, diff --git a/src/types/object.rs b/src/types/object.rs index 107ee0e7..ac8620e1 100644 --- a/src/types/object.rs +++ b/src/types/object.rs @@ -104,8 +104,10 @@ pub struct RadioLink<'s> { #[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>, } #[derive(Debug, PartialEq)]