Compare commits
No commits in common. "ab337308308dcebcdea1825f8672625e30433be6" and "51748afd4150d2e9b219ac99d43f53cb1ffc1889" have entirely different histories.
ab33730830
...
51748afd41
@ -4,10 +4,6 @@
|
|||||||
|
|
||||||
[[((baz)]]
|
[[((baz)]]
|
||||||
|
|
||||||
[[(lo
|
|
||||||
rem)]]
|
|
||||||
|
|
||||||
# These become fuzzy
|
# These become fuzzy
|
||||||
[[(foo) ]]
|
[[(foo) ]]
|
||||||
[[ (foo)]]
|
[[ (foo)]]
|
||||||
[[(foo)::3]]
|
|
||||||
|
@ -1,6 +1 @@
|
|||||||
[[#foo]]
|
[[#foo]]
|
||||||
|
|
||||||
[[#fo
|
|
||||||
o]]
|
|
||||||
|
|
||||||
[[#foo::3]]
|
|
||||||
|
@ -1,18 +1 @@
|
|||||||
[[file:simple.org]]
|
[[file:simple.org]]
|
||||||
[[file:sim ple.org]]
|
|
||||||
|
|
||||||
[[file:simp
|
|
||||||
le.org]]
|
|
||||||
|
|
||||||
[[file:simple.org::3]]
|
|
||||||
[[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
|
|
||||||
bar]]
|
|
||||||
[[file:simple.org::foo::bar]]
|
|
||||||
[[file:simple.org::/foo/]]
|
|
||||||
|
@ -1,6 +1 @@
|
|||||||
[[elisp.org]]
|
[[elisp.org]]
|
||||||
|
|
||||||
[[eli
|
|
||||||
sp.org]]
|
|
||||||
|
|
||||||
[[elisp.org::3]]
|
|
||||||
|
@ -1,6 +1 @@
|
|||||||
[[id:83986bdf-987c-465d-8851-44cb4c02a86c]]
|
[[id:83986bdf-987c-465d-8851-44cb4c02a86c]]
|
||||||
|
|
||||||
[[id:83986bdf-987c-465d
|
|
||||||
-8851-44cb4c02a86c]]
|
|
||||||
|
|
||||||
[[id:83986bdf-987c-465d-8851-44cb4c02a86c::foo]]
|
|
||||||
|
@ -1,6 +1 @@
|
|||||||
[[shell:foo]]
|
[[shell:foo]]
|
||||||
|
|
||||||
[[shell:fo
|
|
||||||
o]]
|
|
||||||
|
|
||||||
[[shell:foo::3]]
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
#+LINK: foo https://foo.bar/baz#%s
|
|
||||||
[[foo::lorem]]
|
|
||||||
[[foo::ipsum][dolar]]
|
|
||||||
|
|
||||||
[[cat::bat]]
|
|
||||||
#+LINK: cat dog%s
|
|
@ -2,9 +2,7 @@ use std::fmt::Debug;
|
|||||||
|
|
||||||
use super::diff::DiffStatus;
|
use super::diff::DiffStatus;
|
||||||
use super::sexp::Token;
|
use super::sexp::Token;
|
||||||
use super::util::get_property;
|
|
||||||
use super::util::get_property_quoted_string;
|
use super::util::get_property_quoted_string;
|
||||||
use super::util::get_property_unquoted_atom;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum EmacsField<'s> {
|
pub(crate) enum EmacsField<'s> {
|
||||||
@ -34,36 +32,7 @@ pub(crate) fn compare_identity() -> () {
|
|||||||
()
|
()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assert that the emacs value is always nil or absent.
|
pub(crate) fn compare_property_quoted_string<'b, 's, 'x, R, RG: Fn(R) -> Option<&'s str>>(
|
||||||
///
|
|
||||||
/// This is usually used for fields which, in my testing, are always nil. Using this compare function instead of simply doing a compare_noop will enable us to be alerted when we finally come across an org-mode document that has a value other than nil for the property.
|
|
||||||
pub(crate) fn compare_property_always_nil<'b, 's, 'x, R, RG>(
|
|
||||||
emacs: &'b Token<'s>,
|
|
||||||
_rust_node: R,
|
|
||||||
emacs_field: &'x str,
|
|
||||||
_rust_value_getter: RG,
|
|
||||||
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> {
|
|
||||||
let value = get_property(emacs, emacs_field)?;
|
|
||||||
if value.is_some() {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} was expected to always be nil: {:?}",
|
|
||||||
emacs_field, value
|
|
||||||
));
|
|
||||||
Ok(Some((this_status, message)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn compare_property_quoted_string<
|
|
||||||
'b,
|
|
||||||
's,
|
|
||||||
'x,
|
|
||||||
R,
|
|
||||||
RV: AsRef<str> + std::fmt::Debug,
|
|
||||||
RG: Fn(R) -> Option<RV>,
|
|
||||||
>(
|
|
||||||
emacs: &'b Token<'s>,
|
emacs: &'b Token<'s>,
|
||||||
rust_node: R,
|
rust_node: R,
|
||||||
emacs_field: &'x str,
|
emacs_field: &'x str,
|
||||||
@ -71,27 +40,7 @@ pub(crate) fn compare_property_quoted_string<
|
|||||||
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> {
|
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> {
|
||||||
let value = get_property_quoted_string(emacs, emacs_field)?;
|
let value = get_property_quoted_string(emacs, emacs_field)?;
|
||||||
let rust_value = rust_value_getter(rust_node);
|
let rust_value = rust_value_getter(rust_node);
|
||||||
if rust_value.as_ref().map(|s| s.as_ref()) != value.as_ref().map(String::as_str) {
|
if !rust_value.eq(&value.as_ref().map(String::as_str)) {
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
|
||||||
emacs_field, value, rust_value
|
|
||||||
));
|
|
||||||
Ok(Some((this_status, message)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn compare_property_unquoted_atom<'b, 's, 'x, R, RG: Fn(R) -> Option<&'s str>>(
|
|
||||||
emacs: &'b Token<'s>,
|
|
||||||
rust_node: R,
|
|
||||||
emacs_field: &'x str,
|
|
||||||
rust_value_getter: RG,
|
|
||||||
) -> Result<Option<(DiffStatus, Option<String>)>, Box<dyn std::error::Error>> {
|
|
||||||
let value = get_property_unquoted_atom(emacs, emacs_field)?;
|
|
||||||
let rust_value = rust_value_getter(rust_node);
|
|
||||||
if rust_value != value {
|
|
||||||
let this_status = DiffStatus::Bad;
|
let this_status = DiffStatus::Bad;
|
||||||
let message = Some(format!(
|
let message = Some(format!(
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
|
@ -4,9 +4,8 @@ use std::collections::BTreeSet;
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use super::compare_field::compare_identity;
|
use super::compare_field::compare_identity;
|
||||||
use super::compare_field::compare_property_always_nil;
|
use super::compare_field::compare_noop;
|
||||||
use super::compare_field::compare_property_quoted_string;
|
use super::compare_field::compare_property_quoted_string;
|
||||||
use super::compare_field::compare_property_unquoted_atom;
|
|
||||||
use super::elisp_fact::ElispFact;
|
use super::elisp_fact::ElispFact;
|
||||||
use super::elisp_fact::GetElispFact;
|
use super::elisp_fact::GetElispFact;
|
||||||
use super::sexp::unquote;
|
use super::sexp::unquote;
|
||||||
@ -2771,41 +2770,41 @@ fn compare_regular_link<'b, 's>(
|
|||||||
(
|
(
|
||||||
EmacsField::Required(":type"),
|
EmacsField::Required(":type"),
|
||||||
|r| {
|
|r| {
|
||||||
match &r.link_type {
|
match r.link_type {
|
||||||
LinkType::File => Some(Cow::Borrowed("file")),
|
LinkType::File => Some("file"),
|
||||||
LinkType::Protocol(protocol) => Some(protocol.clone()),
|
LinkType::Protocol(protocol) => Some(protocol),
|
||||||
LinkType::Id => Some(Cow::Borrowed("id")),
|
LinkType::Id => Some("id"),
|
||||||
LinkType::CustomId => Some(Cow::Borrowed("custom-id")),
|
LinkType::CustomId => Some("custom-id"),
|
||||||
LinkType::CodeRef => Some(Cow::Borrowed("coderef")),
|
LinkType::CodeRef => Some("coderef"),
|
||||||
LinkType::Fuzzy => Some(Cow::Borrowed("fuzzy")),
|
LinkType::Fuzzy => Some("fuzzy"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
compare_property_quoted_string
|
compare_property_quoted_string
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
EmacsField::Required(":path"),
|
EmacsField::Required(":path"),
|
||||||
|r| Some(r.get_path()),
|
|r| Some(r.path),
|
||||||
compare_property_quoted_string
|
compare_property_quoted_string
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
EmacsField::Required(":format"),
|
EmacsField::Required(":format"),
|
||||||
|_| Some("bracket"),
|
compare_identity,
|
||||||
compare_property_unquoted_atom
|
compare_noop
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
EmacsField::Required(":raw-link"),
|
EmacsField::Required(":raw-link"),
|
||||||
|r| Some(r.get_raw_link()),
|
|r| Some(r.raw_link),
|
||||||
compare_property_quoted_string
|
compare_property_quoted_string
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
EmacsField::Required(":application"),
|
EmacsField::Required(":application"),
|
||||||
compare_identity,
|
compare_identity,
|
||||||
compare_property_always_nil
|
compare_noop
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
EmacsField::Required(":search-option"),
|
EmacsField::Required(":search-option"),
|
||||||
|r| r.get_search_option(),
|
compare_identity,
|
||||||
compare_property_quoted_string
|
compare_noop
|
||||||
)
|
)
|
||||||
)? {
|
)? {
|
||||||
this_status = new_status;
|
this_status = new_status;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use std::collections::BTreeMap;
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use super::FileAccessInterface;
|
use super::FileAccessInterface;
|
||||||
@ -38,18 +37,6 @@ pub struct GlobalSettings<'g, 's> {
|
|||||||
///
|
///
|
||||||
/// Corresponds to org-coderef-label-format elisp variable.
|
/// Corresponds to org-coderef-label-format elisp variable.
|
||||||
pub coderef_label_format: &'g str,
|
pub coderef_label_format: &'g str,
|
||||||
|
|
||||||
/// The allowed protocols for links (for example, the "https" in "https://foo.bar/").
|
|
||||||
///
|
|
||||||
/// Corresponds to org-link-parameters elisp variable.
|
|
||||||
pub link_parameters: &'g [&'g str],
|
|
||||||
|
|
||||||
/// Link templates where the key is the document text and the value is the replacement.
|
|
||||||
///
|
|
||||||
/// For example, `"foo": "bar%s"` will replace `[[foo::baz]]` with `[[barbaz]]`
|
|
||||||
///
|
|
||||||
/// This is set by including #+LINK in the org-mode document.
|
|
||||||
pub link_templates: BTreeMap<String, String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8;
|
pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8;
|
||||||
@ -68,8 +55,6 @@ impl<'g, 's> GlobalSettings<'g, 's> {
|
|||||||
odd_levels_only: HeadlineLevelFilter::default(),
|
odd_levels_only: HeadlineLevelFilter::default(),
|
||||||
footnote_section: "Footnotes",
|
footnote_section: "Footnotes",
|
||||||
coderef_label_format: "(ref:%s)",
|
coderef_label_format: "(ref:%s)",
|
||||||
link_parameters: &DEFAULT_ORG_LINK_PARAMETERS,
|
|
||||||
link_templates: BTreeMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,29 +76,3 @@ impl Default for HeadlineLevelFilter {
|
|||||||
HeadlineLevelFilter::OddEven
|
HeadlineLevelFilter::OddEven
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_ORG_LINK_PARAMETERS: [&'static str; 23] = [
|
|
||||||
"id",
|
|
||||||
"eww",
|
|
||||||
"rmail",
|
|
||||||
"mhe",
|
|
||||||
"irc",
|
|
||||||
"info",
|
|
||||||
"gnus",
|
|
||||||
"docview",
|
|
||||||
"bibtex",
|
|
||||||
"bbdb",
|
|
||||||
"w3m",
|
|
||||||
"doi",
|
|
||||||
"file+sys",
|
|
||||||
"file+emacs",
|
|
||||||
"shell",
|
|
||||||
"news",
|
|
||||||
"mailto",
|
|
||||||
"https",
|
|
||||||
"http",
|
|
||||||
"ftp",
|
|
||||||
"help",
|
|
||||||
"file",
|
|
||||||
"elisp",
|
|
||||||
];
|
|
||||||
|
@ -131,8 +131,7 @@ fn document_org_source<'b, 'g, 'r, 's>(
|
|||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
for setup_file in setup_files.iter().map(String::as_str) {
|
for setup_file in setup_files.iter().map(String::as_str) {
|
||||||
let (_, setup_file_settings) =
|
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(
|
nom::Err::Error(CustomError::MyError(MyError(
|
||||||
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
||||||
.into(),
|
.into(),
|
||||||
@ -142,8 +141,7 @@ fn document_org_source<'b, 'g, 'r, 's>(
|
|||||||
}
|
}
|
||||||
final_settings.extend(document_settings);
|
final_settings.extend(document_settings);
|
||||||
let new_settings = apply_in_buffer_settings(final_settings, context.get_global_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(
|
nom::Err::Error(CustomError::MyError(MyError(
|
||||||
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
"TODO: make this take an owned string so I can dump err.to_string() into it."
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -2,17 +2,8 @@ use nom::branch::alt;
|
|||||||
use nom::bytes::complete::is_not;
|
use nom::bytes::complete::is_not;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
use nom::bytes::complete::take_until;
|
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::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::multi::separated_list0;
|
||||||
use nom::sequence::tuple;
|
|
||||||
|
|
||||||
use super::keyword::filtered_keyword;
|
use super::keyword::filtered_keyword;
|
||||||
use super::keyword_todo::todo_keywords;
|
use super::keyword_todo::todo_keywords;
|
||||||
@ -84,7 +75,6 @@ fn in_buffer_settings_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSou
|
|||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
|
||||||
pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|
||||||
keywords: Vec<Keyword<'sf>>,
|
keywords: Vec<Keyword<'sf>>,
|
||||||
original_settings: &'g GlobalSettings<'g, 's>,
|
original_settings: &'g GlobalSettings<'g, 's>,
|
||||||
@ -123,22 +113,10 @@ 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)
|
Ok(new_settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply in-buffer settings that do not impact parsing and therefore can be applied after parsing.
|
/// 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>(
|
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(
|
||||||
document: &mut Document<'s>,
|
document: &mut Document<'s>,
|
||||||
) -> Result<(), &'static str> {
|
) -> Result<(), &'static str> {
|
||||||
@ -157,30 +135,6 @@ pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(
|
|||||||
Ok(())
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
use std::borrow::Cow;
|
|
||||||
use std::ops::RangeBounds;
|
use std::ops::RangeBounds;
|
||||||
|
|
||||||
use nom::Compare;
|
use nom::Compare;
|
||||||
@ -185,18 +184,6 @@ 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>
|
impl<'s, R> Slice<R> for OrgSource<'s>
|
||||||
where
|
where
|
||||||
R: RangeBounds<usize>,
|
R: RangeBounds<usize>,
|
||||||
|
@ -32,6 +32,33 @@ use crate::parser::util::get_consumed;
|
|||||||
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
||||||
use crate::types::PlainLink;
|
use crate::types::PlainLink;
|
||||||
|
|
||||||
|
// TODO: Make this a user-provided variable corresponding to elisp's org-link-parameters
|
||||||
|
const ORG_LINK_PARAMETERS: [&'static str; 23] = [
|
||||||
|
"id",
|
||||||
|
"eww",
|
||||||
|
"rmail",
|
||||||
|
"mhe",
|
||||||
|
"irc",
|
||||||
|
"info",
|
||||||
|
"gnus",
|
||||||
|
"docview",
|
||||||
|
"bibtex",
|
||||||
|
"bbdb",
|
||||||
|
"w3m",
|
||||||
|
"doi",
|
||||||
|
"file+sys",
|
||||||
|
"file+emacs",
|
||||||
|
"shell",
|
||||||
|
"news",
|
||||||
|
"mailto",
|
||||||
|
"https",
|
||||||
|
"http",
|
||||||
|
"ftp",
|
||||||
|
"help",
|
||||||
|
"file",
|
||||||
|
"elisp",
|
||||||
|
];
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn plain_link<'b, 'g, 'r, 's>(
|
pub(crate) fn plain_link<'b, 'g, 'r, 's>(
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
@ -86,11 +113,12 @@ fn post<'b, 'g, 'r, 's>(
|
|||||||
|
|
||||||
#[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>,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
for link_parameter in context.get_global_settings().link_parameters {
|
// TODO: This should be defined by org-link-parameters
|
||||||
let result = tag_no_case::<_, _, CustomError<_>>(*link_parameter)(input);
|
for link_parameter in ORG_LINK_PARAMETERS {
|
||||||
|
let result = tag_no_case::<_, _, CustomError<_>>(link_parameter)(input);
|
||||||
match result {
|
match result {
|
||||||
Ok((remaining, ent)) => {
|
Ok((remaining, ent)) => {
|
||||||
return Ok((remaining, ent));
|
return Ok((remaining, ent));
|
||||||
|
@ -1,27 +1,20 @@
|
|||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::escaped;
|
use nom::bytes::complete::escaped;
|
||||||
use nom::bytes::complete::tag;
|
use nom::bytes::complete::tag;
|
||||||
use nom::bytes::complete::take_till1;
|
use nom::bytes::complete::take_till1;
|
||||||
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::map;
|
|
||||||
use nom::combinator::map_parser;
|
use nom::combinator::map_parser;
|
||||||
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::rest;
|
||||||
use nom::combinator::verify;
|
use nom::combinator::verify;
|
||||||
use nom::multi::many_till;
|
use nom::multi::many_till;
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
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::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
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;
|
||||||
@ -30,8 +23,6 @@ use crate::context::ContextElement;
|
|||||||
use crate::context::ExitClass;
|
use crate::context::ExitClass;
|
||||||
use crate::context::ExitMatcherNode;
|
use crate::context::ExitMatcherNode;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::CustomError;
|
|
||||||
use crate::error::MyError;
|
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::types::LinkType;
|
use crate::types::LinkType;
|
||||||
use crate::types::Object;
|
use crate::types::Object;
|
||||||
@ -66,7 +57,6 @@ fn regular_link_without_description<'b, 'g, 'r, 's>(
|
|||||||
link_type: path.link_type,
|
link_type: path.link_type,
|
||||||
path: path.path,
|
path: path.path,
|
||||||
raw_link: path.raw_link,
|
raw_link: path.raw_link,
|
||||||
search_option: path.search_option,
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -91,22 +81,19 @@ fn regular_link_with_description<'b, 'g, 'r, 's>(
|
|||||||
link_type: path.link_type,
|
link_type: path.link_type,
|
||||||
path: path.path,
|
path: path.path,
|
||||||
raw_link: path.raw_link,
|
raw_link: path.raw_link,
|
||||||
search_option: path.search_option,
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct PathReg<'s> {
|
struct PathReg<'s> {
|
||||||
link_type: LinkType<'s>,
|
link_type: LinkType<'s>,
|
||||||
path: Cow<'s, str>,
|
path: &'s str,
|
||||||
raw_link: Cow<'s, str>,
|
raw_link: &'s str,
|
||||||
search_option: Option<Cow<'s, str>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn pathreg<'b, 'g, 'r, 's>(
|
fn pathreg<'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>, PathReg<'s>> {
|
) -> Res<OrgSource<'s>, PathReg<'s>> {
|
||||||
let (remaining, path) = map_parser(
|
let (remaining, path) = map_parser(
|
||||||
@ -118,136 +105,29 @@ fn pathreg<'b, 'g, 'r, 's>(
|
|||||||
'\\',
|
'\\',
|
||||||
anychar,
|
anychar,
|
||||||
),
|
),
|
||||||
parser_with_context!(parse_path_reg)(context),
|
parse_path_reg,
|
||||||
)(input)?;
|
)(input)?;
|
||||||
Ok((remaining, path))
|
Ok((remaining, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_path_reg<'b, 'g, 'r, 's>(
|
fn parse_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, PathReg<'s>> {
|
|
||||||
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((
|
alt((
|
||||||
file_path_reg,
|
file_path_reg,
|
||||||
id_path_reg,
|
id_path_reg,
|
||||||
custom_id_path_reg,
|
custom_id_path_reg,
|
||||||
code_ref_path_reg,
|
code_ref_path_reg,
|
||||||
parser_with_context!(protocol_path_reg)(context),
|
|
||||||
fuzzy_path_reg,
|
fuzzy_path_reg,
|
||||||
))(input)
|
))(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>> {
|
fn file_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
|
||||||
let (remaining, (raw_link, (_, path, search_option))) = consumed(tuple((
|
let (remaining, (raw_link, (_, path))) = consumed(tuple((tag("file:"), rest)))(input)?;
|
||||||
tag("file:"),
|
|
||||||
recognize(many_till(anychar, alt((peek(tag("::")), eof)))),
|
|
||||||
opt(map(tuple((tag("::"), rest)), |(_, search_option)| {
|
|
||||||
search_option
|
|
||||||
})),
|
|
||||||
)))(input)?;
|
|
||||||
Ok((
|
Ok((
|
||||||
remaining,
|
remaining,
|
||||||
PathReg {
|
PathReg {
|
||||||
link_type: LinkType::File,
|
link_type: LinkType::File,
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
raw_link: raw_link.into(),
|
raw_link: raw_link.into(),
|
||||||
search_option: search_option
|
|
||||||
.map(Into::<&str>::into)
|
|
||||||
.map(Into::<Cow<str>>::into),
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -260,7 +140,6 @@ fn id_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
|
|||||||
link_type: LinkType::Id,
|
link_type: LinkType::Id,
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
raw_link: raw_link.into(),
|
raw_link: raw_link.into(),
|
||||||
search_option: None,
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -273,7 +152,6 @@ fn custom_id_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s
|
|||||||
link_type: LinkType::CustomId,
|
link_type: LinkType::CustomId,
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
raw_link: raw_link.into(),
|
raw_link: raw_link.into(),
|
||||||
search_option: None,
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -290,27 +168,6 @@ fn code_ref_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>
|
|||||||
link_type: LinkType::CodeRef,
|
link_type: LinkType::CodeRef,
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
raw_link: raw_link.into(),
|
raw_link: raw_link.into(),
|
||||||
search_option: None,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn protocol_path_reg<'b, 'g, 'r, 's>(
|
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, PathReg<'s>> {
|
|
||||||
let (remaining, (raw_link, (protocol, _, path))) = consumed(tuple((
|
|
||||||
parser_with_context!(protocol)(context),
|
|
||||||
tag(":"),
|
|
||||||
rest,
|
|
||||||
)))(input)?;
|
|
||||||
Ok((
|
|
||||||
remaining,
|
|
||||||
PathReg {
|
|
||||||
link_type: LinkType::Protocol(protocol.into()),
|
|
||||||
path: path.into(),
|
|
||||||
raw_link: raw_link.into(),
|
|
||||||
search_option: None,
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -323,7 +180,6 @@ fn fuzzy_path_reg<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PathReg<'s>> {
|
|||||||
link_type: LinkType::Fuzzy,
|
link_type: LinkType::Fuzzy,
|
||||||
path: body.into(),
|
path: body.into(),
|
||||||
raw_link: body.into(),
|
raw_link: body.into(),
|
||||||
search_option: None,
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
use std::borrow::Borrow;
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use super::GetStandardProperties;
|
use super::GetStandardProperties;
|
||||||
use super::StandardProperties;
|
use super::StandardProperties;
|
||||||
|
|
||||||
@ -81,9 +78,8 @@ pub struct PlainText<'s> {
|
|||||||
pub struct RegularLink<'s> {
|
pub struct RegularLink<'s> {
|
||||||
pub source: &'s str,
|
pub source: &'s str,
|
||||||
pub link_type: LinkType<'s>,
|
pub link_type: LinkType<'s>,
|
||||||
pub path: Cow<'s, str>,
|
pub path: &'s str,
|
||||||
pub raw_link: Cow<'s, str>,
|
pub raw_link: &'s str,
|
||||||
pub search_option: Option<Cow<'s, str>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -646,73 +642,9 @@ impl<'s> Timestamp<'s> {
|
|||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum LinkType<'s> {
|
pub enum LinkType<'s> {
|
||||||
File,
|
File,
|
||||||
Protocol(Cow<'s, str>),
|
Protocol(&'s str),
|
||||||
Id,
|
Id,
|
||||||
CustomId,
|
CustomId,
|
||||||
CodeRef,
|
CodeRef,
|
||||||
Fuzzy,
|
Fuzzy,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ParserState {
|
|
||||||
Normal,
|
|
||||||
InWhitespace,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Org-mode treats multiple consecutive whitespace characters as a single space. This function performs that transformation.
|
|
||||||
///
|
|
||||||
/// Example: `orgify_text("foo \t\n bar") == "foo bar"`
|
|
||||||
pub(crate) fn orgify_text<T: AsRef<str>>(raw_text: T) -> String {
|
|
||||||
let raw_text = raw_text.as_ref();
|
|
||||||
let mut ret = String::with_capacity(raw_text.len());
|
|
||||||
let mut state = ParserState::Normal;
|
|
||||||
for c in raw_text.chars() {
|
|
||||||
state = match (&state, c) {
|
|
||||||
(ParserState::Normal, _) if " \t\r\n".contains(c) => {
|
|
||||||
ret.push(' ');
|
|
||||||
ParserState::InWhitespace
|
|
||||||
}
|
|
||||||
(ParserState::InWhitespace, _) if " \t\r\n".contains(c) => ParserState::InWhitespace,
|
|
||||||
(ParserState::Normal, _) => {
|
|
||||||
ret.push(c);
|
|
||||||
ParserState::Normal
|
|
||||||
}
|
|
||||||
(ParserState::InWhitespace, _) => {
|
|
||||||
ret.push(c);
|
|
||||||
ParserState::Normal
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'s> RegularLink<'s> {
|
|
||||||
/// Orgify the raw_link if it contains line breaks.
|
|
||||||
pub fn get_raw_link(&self) -> String {
|
|
||||||
if self.raw_link.contains('\n') {
|
|
||||||
orgify_text(Borrow::<str>::borrow(&self.raw_link))
|
|
||||||
} else {
|
|
||||||
self.raw_link.clone().into_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Orgify the path if it contains line breaks.
|
|
||||||
pub fn get_path(&self) -> String {
|
|
||||||
if self.path.contains('\n') {
|
|
||||||
orgify_text(Borrow::<str>::borrow(&self.path))
|
|
||||||
} else {
|
|
||||||
self.path.clone().into_owned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Orgify the search_option if it contains line breaks.
|
|
||||||
pub fn get_search_option(&self) -> Option<String> {
|
|
||||||
self.search_option.as_ref().map(|search_option| {
|
|
||||||
if search_option.contains('\n') {
|
|
||||||
orgify_text(search_option)
|
|
||||||
} else {
|
|
||||||
search_option.clone().into_owned()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user