Apply the link templates.
This commit is contained in:
@@ -131,7 +131,8 @@ fn document_org_source<'b, 'g, 'r, 's>(
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
for setup_file in setup_files.iter().map(String::as_str) {
|
||||
let (_, setup_file_settings) =
|
||||
scan_for_in_buffer_settings(setup_file.into()).map_err(|_err| {
|
||||
scan_for_in_buffer_settings(setup_file.into()).map_err(|err| {
|
||||
eprintln!("{}", err);
|
||||
nom::Err::Error(CustomError::MyError(MyError(
|
||||
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
||||
.into(),
|
||||
@@ -141,7 +142,8 @@ fn document_org_source<'b, 'g, 'r, 's>(
|
||||
}
|
||||
final_settings.extend(document_settings);
|
||||
let new_settings = apply_in_buffer_settings(final_settings, context.get_global_settings())
|
||||
.map_err(|_err| {
|
||||
.map_err(|err| {
|
||||
eprintln!("{}", err);
|
||||
nom::Err::Error(CustomError::MyError(MyError(
|
||||
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
||||
.into(),
|
||||
|
||||
@@ -2,8 +2,17 @@ use nom::branch::alt;
|
||||
use nom::bytes::complete::is_not;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::bytes::complete::take_until;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::space0;
|
||||
use nom::character::complete::space1;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::multi::many_till;
|
||||
use nom::multi::separated_list0;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::filtered_keyword;
|
||||
use super::keyword_todo::todo_keywords;
|
||||
@@ -75,6 +84,7 @@ fn in_buffer_settings_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSou
|
||||
))(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
||||
keywords: Vec<Keyword<'sf>>,
|
||||
original_settings: &'g GlobalSettings<'g, 's>,
|
||||
@@ -113,10 +123,22 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
||||
}
|
||||
}
|
||||
|
||||
// Link templates
|
||||
for kw in keywords
|
||||
.iter()
|
||||
.filter(|kw| kw.key.eq_ignore_ascii_case("link"))
|
||||
{
|
||||
let (_, (link_key, link_value)) = link_template(kw.value).map_err(|e| e.to_string())?;
|
||||
new_settings
|
||||
.link_templates
|
||||
.insert(link_key.to_owned(), link_value.to_owned());
|
||||
}
|
||||
|
||||
Ok(new_settings)
|
||||
}
|
||||
|
||||
/// Apply in-buffer settings that do not impact parsing and therefore can be applied after parsing.
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(
|
||||
document: &mut Document<'s>,
|
||||
) -> Result<(), &'static str> {
|
||||
@@ -135,6 +157,30 @@ pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn link_template<'s>(input: &'s str) -> Res<&'s str, (&'s str, &'s str)> {
|
||||
let (remaining, key) = map(
|
||||
tuple((
|
||||
space0,
|
||||
recognize(many_till(anychar, peek(alt((space1, line_ending, eof))))),
|
||||
)),
|
||||
|(_, key)| key,
|
||||
)(input)?;
|
||||
|
||||
let (remaining, replacement) = map(
|
||||
tuple((
|
||||
space1,
|
||||
recognize(many_till(
|
||||
anychar,
|
||||
peek(tuple((space0, alt((line_ending, eof))))),
|
||||
)),
|
||||
)),
|
||||
|(_, replacement)| replacement,
|
||||
)(remaining)?;
|
||||
|
||||
Ok((remaining, (key, replacement)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::borrow::Cow;
|
||||
use std::ops::RangeBounds;
|
||||
|
||||
use nom::Compare;
|
||||
@@ -184,6 +185,18 @@ impl<'s> From<OrgSource<'s>> for &'s str {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> From<&OrgSource<'s>> for Cow<'s, str> {
|
||||
fn from(value: &OrgSource<'s>) -> Self {
|
||||
(&value.full_source[value.start..value.end]).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> From<OrgSource<'s>> for Cow<'s, str> {
|
||||
fn from(value: OrgSource<'s>) -> Self {
|
||||
(&value.full_source[value.start..value.end]).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, R> Slice<R> for OrgSource<'s>
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::escaped;
|
||||
use nom::bytes::complete::tag;
|
||||
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;
|
||||
@@ -14,6 +17,7 @@ use nom::combinator::rest;
|
||||
use nom::combinator::verify;
|
||||
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::OrgSource;
|
||||
@@ -26,6 +30,8 @@ use crate::context::ContextElement;
|
||||
use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::types::LinkType;
|
||||
use crate::types::Object;
|
||||
@@ -90,11 +96,12 @@ fn regular_link_with_description<'b, 'g, 'r, 's>(
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PathReg<'s> {
|
||||
link_type: LinkType<'s>,
|
||||
path: &'s str,
|
||||
raw_link: &'s str,
|
||||
search_option: Option<&'s str>,
|
||||
path: Cow<'s, str>,
|
||||
raw_link: Cow<'s, str>,
|
||||
search_option: Option<Cow<'s, str>>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
@@ -120,14 +127,108 @@ fn parse_path_reg<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, PathReg<'s>> {
|
||||
alt((
|
||||
file_path_reg,
|
||||
id_path_reg,
|
||||
custom_id_path_reg,
|
||||
code_ref_path_reg,
|
||||
parser_with_context!(protocol_path_reg)(context),
|
||||
fuzzy_path_reg,
|
||||
if let Some(replaced_link) = apply_link_templates(context, input) {
|
||||
let replaced_input = Into::<OrgSource<'_>>::into(replaced_link.as_str());
|
||||
let (_remaining, link) = alt((
|
||||
file_path_reg,
|
||||
id_path_reg,
|
||||
custom_id_path_reg,
|
||||
code_ref_path_reg,
|
||||
parser_with_context!(protocol_path_reg)(context),
|
||||
fuzzy_path_reg,
|
||||
))(replaced_input)
|
||||
.map_err(|_| {
|
||||
nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No pathreg match after replacement.",
|
||||
)))
|
||||
})?;
|
||||
let remaining = input.take(input.len());
|
||||
let link_type = match link.link_type {
|
||||
LinkType::Protocol(protocol) => LinkType::Protocol(protocol.into_owned().into()),
|
||||
LinkType::File => LinkType::File,
|
||||
LinkType::Id => LinkType::Id,
|
||||
LinkType::CustomId => LinkType::CustomId,
|
||||
LinkType::CodeRef => LinkType::CodeRef,
|
||||
LinkType::Fuzzy => LinkType::Fuzzy,
|
||||
};
|
||||
Ok((
|
||||
remaining,
|
||||
PathReg {
|
||||
link_type,
|
||||
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()),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
alt((
|
||||
file_path_reg,
|
||||
id_path_reg,
|
||||
custom_id_path_reg,
|
||||
code_ref_path_reg,
|
||||
parser_with_context!(protocol_path_reg)(context),
|
||||
fuzzy_path_reg,
|
||||
))(input)
|
||||
}
|
||||
}
|
||||
|
||||
enum ParserState {
|
||||
Normal,
|
||||
Percent,
|
||||
}
|
||||
|
||||
fn apply_link_templates<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Option<String> {
|
||||
let (remaining, key) = opt(map(
|
||||
tuple((
|
||||
recognize(take_until::<_, _, nom::error::Error<_>>("::")),
|
||||
tag("::"),
|
||||
)),
|
||||
|(key, _)| key,
|
||||
))(input)
|
||||
.expect("opt ensures this cannot error.");
|
||||
let key = match key {
|
||||
Some(key) => key,
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let replacement_template = match context.get_global_settings().link_templates.get(key.into()) {
|
||||
Some(template) => template,
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let inject_value = Into::<&str>::into(remaining);
|
||||
let mut ret = String::with_capacity(replacement_template.len() + inject_value.len());
|
||||
let mut state = ParserState::Normal;
|
||||
for c in replacement_template.chars() {
|
||||
state = match (&state, c) {
|
||||
(ParserState::Normal, '%') => ParserState::Percent,
|
||||
(ParserState::Normal, _) => {
|
||||
ret.push(c);
|
||||
ParserState::Normal
|
||||
}
|
||||
(ParserState::Percent, 's') => {
|
||||
ret.push_str(inject_value);
|
||||
ParserState::Normal
|
||||
}
|
||||
(ParserState::Percent, _) => {
|
||||
panic!("Unhandled percent value: {}", c)
|
||||
}
|
||||
};
|
||||
}
|
||||
// Handle lingering state
|
||||
match state {
|
||||
ParserState::Percent => {
|
||||
ret.push('%');
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Some(ret)
|
||||
}
|
||||
|
||||
fn file_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
|
||||
@@ -144,7 +245,9 @@ fn file_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
|
||||
link_type: LinkType::File,
|
||||
path: path.into(),
|
||||
raw_link: raw_link.into(),
|
||||
search_option: search_option.map(Into::<&str>::into),
|
||||
search_option: search_option
|
||||
.map(Into::<&str>::into)
|
||||
.map(Into::<Cow<str>>::into),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user