Merge branch 'plain_link_properties'
This commit is contained in:
commit
488372b070
@ -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
|
13
org_mode_samples/object/plain_link/multiple_slashes.org
Normal file
13
org_mode_samples/object/plain_link/multiple_slashes.org
Normal 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
|
4
org_mode_samples/object/plain_link/relative_path.org
Normal file
4
org_mode_samples/object/plain_link/relative_path.org
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# These do not become links
|
||||||
|
./simple.org::foo
|
||||||
|
../simple.org::foo
|
||||||
|
/simple.org::foo
|
16
org_mode_samples/object/plain_link/search_option.org
Normal file
16
org_mode_samples/object/plain_link/search_option.org
Normal 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
|
8
org_mode_samples/object/plain_link/template.org
Normal file
8
org_mode_samples/object/plain_link/template.org
Normal 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
|
2
org_mode_samples/object/regular_link/application.org
Normal file
2
org_mode_samples/object/regular_link/application.org
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[[file+sys://foo]]
|
||||||
|
[[file+emacs://foo]]
|
20
org_mode_samples/object/regular_link/multiple_slashes.org
Normal file
20
org_mode_samples/object/regular_link/multiple_slashes.org
Normal 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]]
|
2
org_mode_samples/object/regular_link/search_option.org
Normal file
2
org_mode_samples/object/regular_link/search_option.org
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# Does not become a search option because it is inside parenthesis.
|
||||||
|
[[https://en.wikipedia.org/wiki/Shebang_(Uni::x)]]
|
@ -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,
|
||||||
|
@ -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(),
|
||||||
))))
|
))))
|
||||||
|
@ -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()
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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(),
|
||||||
|
))))
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user