Compare commits
No commits in common. "c86d1000c035b105adaadbddf57d52c0f3b17bbc" and "dea3721b1cf301235389566a137574b36c21af39" have entirely different histories.
c86d1000c0
...
dea3721b1c
@ -1,42 +0,0 @@
|
|||||||
* Elisp Structure
|
|
||||||
| Keyword | Single | Double | Single Optval | Double Optval |
|
|
||||||
|---------+---------------+---------------+---------------+---------------|
|
|
||||||
| CAPTION | objtree | objtree | objtree | objtree |
|
|
||||||
| DATA | quoted(:name) | quoted(:name) | - | - |
|
|
||||||
| HEADER | list(quoted) | list(quoted) | - | - |
|
|
||||||
| NAME | quoted(:name) | quoted(:name) | - | - |
|
|
||||||
| PLOT | quoted(:plot) | quoted(:plot) | - | - |
|
|
||||||
| RESULTS | optional pair | optional pair | optional pair | optional pair |
|
|
||||||
* types
|
|
||||||
** objtree
|
|
||||||
Outer list: 1 per keyword
|
|
||||||
next list: first entry = list of objects for value. remaining entries = optval
|
|
||||||
** list(quoted)
|
|
||||||
List of quoted strings, 1 per keyword
|
|
||||||
** quoted(NAME)
|
|
||||||
Quoted string under the NAME property (for example quoted(:name))
|
|
||||||
** optional pair
|
|
||||||
When optval is supplied this is an alist with the field value being the real value and the 3nd value being the optval.
|
|
||||||
#+begin_src elisp
|
|
||||||
("*f*" . "*bar*")
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
When optval is not supplied this is a list containing a single string of the last occurrence of this keyword.
|
|
||||||
#+begin_src elisp
|
|
||||||
("*c*")
|
|
||||||
#+end_src
|
|
||||||
* Default settings
|
|
||||||
#+begin_src text
|
|
||||||
org-element-dual-keywords ("CAPTION" "RESULTS")
|
|
||||||
org-element-parsed-keywords ("CAPTION")
|
|
||||||
org-element-multiple-keywords ("CAPTION" "HEADER")
|
|
||||||
org-babel-results-keyword "RESULTS"
|
|
||||||
#+end_src
|
|
||||||
* Analysis
|
|
||||||
We don't have an example of a parsed non-dual keyword
|
|
||||||
|
|
||||||
Looks like multiple triggers list 1 per keyword
|
|
||||||
|
|
||||||
dual triggers support for optval
|
|
||||||
|
|
||||||
parsed triggers objects
|
|
@ -1,31 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
#
|
|
||||||
set -euo pipefail
|
|
||||||
IFS=$'\n\t'
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
|
|
||||||
file_path="${DIR}/test_document.org"
|
|
||||||
|
|
||||||
for TARGET_VARIABLE in RESULTS CAPTION HEADER DATA NAME PLOT; do
|
|
||||||
INIT_SCRIPT=$(cat <<EOF
|
|
||||||
(progn
|
|
||||||
(erase-buffer)
|
|
||||||
(require 'org)
|
|
||||||
(defun org-table-align () t)
|
|
||||||
(setq vc-handled-backends nil)
|
|
||||||
(find-file "/input/${file_path}")
|
|
||||||
(org-mode)
|
|
||||||
(replace-regexp-in-region "foo" "${TARGET_VARIABLE}")
|
|
||||||
(message "%s" (pp-to-string (org-element-parse-buffer)))
|
|
||||||
)
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
docker run --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -w /input --entrypoint "" organic-test emacs -q --no-site-file --no-splash --batch --eval "$INIT_SCRIPT" 2> "${DIR}/${TARGET_VARIABLE}"
|
|
||||||
done
|
|
||||||
|
|
||||||
# exec docker run --init --rm -i -t --mount type=tmpfs,destination=/tmp -v "/:/input:ro" -w /input --entrypoint "" organic-test emacs -q --no-site-file --no-splash --eval "$INIT_SCRIPT"
|
|
||||||
|
|
||||||
# org-element-dual-keywords ("CAPTION" "RESULTS")
|
|
||||||
# org-element-parsed-keywords ("CAPTION")
|
|
||||||
# org-element-multiple-keywords ("CAPTION" "HEADER")
|
|
||||||
# org-babel-results-keyword "RESULTS"
|
|
@ -1,25 +0,0 @@
|
|||||||
# Single instance
|
|
||||||
#+foo: *a*
|
|
||||||
#+begin_example
|
|
||||||
|
|
||||||
#+end_example
|
|
||||||
|
|
||||||
# Two instances
|
|
||||||
#+foo: *b*
|
|
||||||
#+foo: *c*
|
|
||||||
#+begin_example
|
|
||||||
|
|
||||||
#+end_example
|
|
||||||
|
|
||||||
# Single with optval
|
|
||||||
#+foo[*bar*]: *d*
|
|
||||||
#+begin_example
|
|
||||||
|
|
||||||
#+end_example
|
|
||||||
|
|
||||||
# Two with optval
|
|
||||||
#+foo[*bar*]: *e*
|
|
||||||
#+foo[*bar*]: *f*
|
|
||||||
#+begin_example
|
|
||||||
|
|
||||||
#+end_example
|
|
@ -1,16 +0,0 @@
|
|||||||
#+results[foo]: bar
|
|
||||||
#+results[lorem]: ipsum
|
|
||||||
#+begin_example
|
|
||||||
baz
|
|
||||||
#+end_example
|
|
||||||
|
|
||||||
#+caption[lorem]: ipsum
|
|
||||||
#+caption[foo]: bar
|
|
||||||
#+begin_example
|
|
||||||
baz
|
|
||||||
#+end_example
|
|
||||||
|
|
||||||
#+header[foo]: bar
|
|
||||||
#+begin_example
|
|
||||||
baz
|
|
||||||
#+end_example
|
|
@ -16,6 +16,7 @@ use super::util::get_property_unquoted_atom;
|
|||||||
use crate::types::AstNode;
|
use crate::types::AstNode;
|
||||||
use crate::types::CharOffsetInLine;
|
use crate::types::CharOffsetInLine;
|
||||||
use crate::types::LineNumber;
|
use crate::types::LineNumber;
|
||||||
|
use crate::types::Object;
|
||||||
use crate::types::RetainLabels;
|
use crate::types::RetainLabels;
|
||||||
use crate::types::SwitchNumberLines;
|
use crate::types::SwitchNumberLines;
|
||||||
|
|
||||||
@ -287,107 +288,6 @@ pub(crate) fn compare_property_set_of_quoted_string<
|
|||||||
Ok(ComparePropertiesResult::NoChange)
|
Ok(ComparePropertiesResult::NoChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compare_property_optional_pair<
|
|
||||||
'b,
|
|
||||||
's,
|
|
||||||
'x,
|
|
||||||
R,
|
|
||||||
RV: AsRef<str> + std::fmt::Debug,
|
|
||||||
ROV: AsRef<str> + std::fmt::Debug,
|
|
||||||
RG: Fn(R) -> Option<(Option<ROV>, RV)>,
|
|
||||||
>(
|
|
||||||
_source: &'s str,
|
|
||||||
emacs: &'b Token<'s>,
|
|
||||||
rust_node: R,
|
|
||||||
emacs_field: &'x str,
|
|
||||||
rust_value_getter: RG,
|
|
||||||
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
|
|
||||||
let value = get_property(emacs, emacs_field)?
|
|
||||||
.map(Token::as_list)
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?;
|
|
||||||
let rust_value = rust_value_getter(rust_node);
|
|
||||||
match (value, &rust_value) {
|
|
||||||
(None, None) => {}
|
|
||||||
(None, Some(_)) | (Some(_), None) => {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
|
||||||
emacs_field, value, rust_value
|
|
||||||
));
|
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
|
||||||
}
|
|
||||||
(Some(el), Some((Some(_), _))) if el.len() != 3 => {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
|
||||||
emacs_field, value, rust_value
|
|
||||||
));
|
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
|
||||||
}
|
|
||||||
(Some(el), Some((None, _))) if el.len() != 1 => {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
|
||||||
emacs_field, value, rust_value
|
|
||||||
));
|
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
|
||||||
}
|
|
||||||
(Some(el), Some((Some(orl), rl))) => {
|
|
||||||
let e = el
|
|
||||||
.first()
|
|
||||||
.map(Token::as_atom)
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
|
||||||
.map(unquote)
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
|
||||||
.expect("Above match proved length to be 3.");
|
|
||||||
let oe = el
|
|
||||||
.get(2)
|
|
||||||
.map(Token::as_atom)
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
|
||||||
.map(unquote)
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
|
||||||
.expect("Above match proved length to be 3.");
|
|
||||||
let r = rl.as_ref();
|
|
||||||
let or = orl.as_ref();
|
|
||||||
if e != r {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}. Full list: {:?} != {:?}",
|
|
||||||
emacs_field, e, r, value, rust_value
|
|
||||||
));
|
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
|
||||||
}
|
|
||||||
if oe != or {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}. Full list: {:?} != {:?}",
|
|
||||||
emacs_field, e, r, value, rust_value
|
|
||||||
));
|
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Some(el), Some((None, rl))) => {
|
|
||||||
let e = el
|
|
||||||
.first()
|
|
||||||
.map(Token::as_atom)
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
|
||||||
.map(unquote)
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
|
||||||
.expect("Above match proved length to be 1.");
|
|
||||||
let r = rl.as_ref();
|
|
||||||
if e != r {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}. Full list: {:?} != {:?}",
|
|
||||||
emacs_field, e, r, value, rust_value
|
|
||||||
));
|
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(ComparePropertiesResult::NoChange)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn compare_property_boolean<'b, 's, 'x, R, RG: Fn(R) -> bool>(
|
pub(crate) fn compare_property_boolean<'b, 's, 'x, R, RG: Fn(R) -> bool>(
|
||||||
_source: &'s str,
|
_source: &'s str,
|
||||||
emacs: &'b Token<'s>,
|
emacs: &'b Token<'s>,
|
||||||
@ -512,133 +412,118 @@ where
|
|||||||
Ok(ComparePropertiesResult::NoChange)
|
Ok(ComparePropertiesResult::NoChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compare_property_object_tree<
|
/// Special compare used for affiliate keywords that are parsed as objects.
|
||||||
|
///
|
||||||
|
/// Org-mode seems to store these as a 3-deep list:
|
||||||
|
/// - Outer list with 1 element per #+caption keyword (or other parsed keyword).
|
||||||
|
/// - Middle list which has:
|
||||||
|
/// - first element is a list of objects representing the value after the colon.
|
||||||
|
/// - every additional element is a list of objects from inside the square brackets (the optional value).
|
||||||
|
pub(crate) fn compare_property_list_of_list_of_list_of_ast_nodes<
|
||||||
'b,
|
'b,
|
||||||
's,
|
's,
|
||||||
'x,
|
'x,
|
||||||
R,
|
R,
|
||||||
RV: std::fmt::Debug + 'b,
|
RG: Fn(R) -> Option<&'b Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>>,
|
||||||
ROV: std::fmt::Debug + 'b,
|
|
||||||
RI: Iterator<Item = &'b (Option<Vec<ROV>>, Vec<RV>)> + ExactSizeIterator + std::fmt::Debug,
|
|
||||||
RG: Fn(R) -> Option<RI>,
|
|
||||||
>(
|
>(
|
||||||
source: &'s str,
|
source: &'s str,
|
||||||
emacs: &'b Token<'s>,
|
emacs: &'b Token<'s>,
|
||||||
rust_node: R,
|
rust_node: R,
|
||||||
emacs_field: &'x str,
|
emacs_field: &'x str,
|
||||||
rust_value_getter: RG,
|
rust_value_getter: RG,
|
||||||
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>>
|
) -> Result<ComparePropertiesResult<'b, 's>, Box<dyn std::error::Error>> {
|
||||||
where
|
// TODO: Replace Object<'s> with generics. I hard-coded Object in to make lifetimes easier.
|
||||||
AstNode<'b, 's>: From<&'b RV>,
|
let rust_value = rust_value_getter(rust_node);
|
||||||
AstNode<'b, 's>: From<&'b ROV>,
|
|
||||||
{
|
|
||||||
let value = get_property(emacs, emacs_field)?
|
let value = get_property(emacs, emacs_field)?
|
||||||
.map(Token::as_list)
|
.map(Token::as_list)
|
||||||
.map_or(Ok(None), |r| r.map(Some))?;
|
.map_or(Ok(None), |r| r.map(Some))?;
|
||||||
let rust_value = rust_value_getter(rust_node);
|
let (value, rust_value) = match (value, rust_value) {
|
||||||
let (outer_emacs_list, outer_rust_list) = match (value, rust_value) {
|
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
return Ok(ComparePropertiesResult::NoChange);
|
return Ok(ComparePropertiesResult::NoChange);
|
||||||
}
|
}
|
||||||
(None, rv @ Some(_)) | (Some(_), rv @ None) => {
|
(None, Some(_)) | (Some(_), None) => {
|
||||||
let this_status = DiffStatus::Bad;
|
let this_status = DiffStatus::Bad;
|
||||||
let message = Some(format!(
|
let message = Some(format!(
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
emacs_field, value, rv
|
emacs_field, value, rust_value
|
||||||
));
|
));
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
}
|
}
|
||||||
(Some(el), Some(rl)) if el.len() != rl.len() => {
|
(Some(value), Some(rust_value)) if value.len() != rust_value.len() => {
|
||||||
let this_status = DiffStatus::Bad;
|
let this_status = DiffStatus::Bad;
|
||||||
let message = Some(format!(
|
let message = Some(format!(
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
emacs_field, el, rl
|
emacs_field, value, rust_value
|
||||||
));
|
));
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
}
|
}
|
||||||
(Some(el), Some(rl)) => (el, rl),
|
(Some(value), Some(rust_value)) => (value, rust_value),
|
||||||
};
|
};
|
||||||
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(outer_rust_list.len());
|
|
||||||
|
|
||||||
for (kw_e, kw_r) in outer_emacs_list.into_iter().zip(outer_rust_list) {
|
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
||||||
let kw_e = kw_e.as_list()?;
|
|
||||||
let child_status_length = kw_r.1.len() + kw_r.0.as_ref().map(|opt| opt.len()).unwrap_or(0);
|
// Iterate the outer lists
|
||||||
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(child_status_length);
|
for (value, (rust_optional, rust_value)) in value.iter().zip(rust_value.iter()) {
|
||||||
if let Some(or) = &kw_r.0 {
|
let mut middle_value = value.as_list()?.iter();
|
||||||
// if optional value
|
// First element of middle list is the mandatory value (the value past the colon).
|
||||||
let mut kw_e = kw_e.into_iter();
|
let mandatory_value = middle_value.next();
|
||||||
// First element is a list representing the mandatory value.
|
let mandatory_value = match mandatory_value {
|
||||||
if let Some(val_e) = kw_e.next() {
|
Some(mandatory_value) => mandatory_value,
|
||||||
let el = val_e.as_list()?;
|
None => {
|
||||||
if el.len() != kw_r.1.len() {
|
|
||||||
let this_status = DiffStatus::Bad;
|
let this_status = DiffStatus::Bad;
|
||||||
let message = Some(format!(
|
let message = Some(format!(
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
emacs_field, kw_e, kw_r
|
emacs_field, value, rust_value
|
||||||
));
|
));
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
}
|
}
|
||||||
for (e, r) in el.into_iter().zip(kw_r.1.iter()) {
|
};
|
||||||
|
|
||||||
|
// Compare optional value
|
||||||
|
if let Some(rust_optional) = rust_optional {
|
||||||
|
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
||||||
|
if rust_optional.len() != middle_value.len() {
|
||||||
|
let this_status = DiffStatus::Bad;
|
||||||
|
let message = Some(format!(
|
||||||
|
"{} optional value length mismatch (emacs != rust) {} != {} | {:?}",
|
||||||
|
emacs_field,
|
||||||
|
middle_value.len(),
|
||||||
|
rust_optional.len(),
|
||||||
|
rust_optional
|
||||||
|
));
|
||||||
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
|
}
|
||||||
|
for (e, r) in middle_value.zip(rust_optional) {
|
||||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||||
}
|
}
|
||||||
} else {
|
if !child_status.is_empty() {
|
||||||
|
let diff_scope = artificial_diff_scope("optional value", child_status)?;
|
||||||
|
full_status.push(diff_scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare mandatory value
|
||||||
|
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
||||||
|
let mandatory_value = mandatory_value.as_list()?;
|
||||||
|
if rust_value.len() != mandatory_value.len() {
|
||||||
let this_status = DiffStatus::Bad;
|
let this_status = DiffStatus::Bad;
|
||||||
let message = Some(format!(
|
let message = Some(format!(
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
"{} mandatory value length mismatch (emacs != rust) {} != {} | {:?}",
|
||||||
emacs_field, kw_e, kw_r
|
emacs_field,
|
||||||
|
mandatory_value.len(),
|
||||||
|
rust_value.len(),
|
||||||
|
rust_value
|
||||||
));
|
));
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
}
|
}
|
||||||
// Remaining elements are the optional value.
|
for (e, r) in mandatory_value.iter().zip(rust_value) {
|
||||||
if kw_e.len() != or.len() {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
|
||||||
emacs_field, kw_e, kw_r
|
|
||||||
));
|
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
|
||||||
}
|
|
||||||
for (e, r) in kw_e.zip(or.iter()) {
|
|
||||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// if no optional value
|
|
||||||
if !kw_e.len() == 1 {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
|
||||||
emacs_field, kw_e, kw_r
|
|
||||||
));
|
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
|
||||||
}
|
|
||||||
|
|
||||||
let e = kw_e
|
|
||||||
.first()
|
|
||||||
.map(Token::as_list)
|
|
||||||
.map_or(Ok(None), |r| r.map(Some))?
|
|
||||||
.expect("The above if-statement proves this will be Some.")
|
|
||||||
.iter();
|
|
||||||
let r = kw_r.1.iter();
|
|
||||||
|
|
||||||
if e.len() != r.len() {
|
|
||||||
let this_status = DiffStatus::Bad;
|
|
||||||
let message = Some(format!(
|
|
||||||
"{} mismatch (emacs != rust) {:?} != {:?}",
|
|
||||||
emacs_field, kw_e, kw_r
|
|
||||||
));
|
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (e, r) in e.zip(r) {
|
|
||||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !child_status.is_empty() {
|
if !child_status.is_empty() {
|
||||||
let diff_scope = artificial_diff_scope("mandatory value", child_status)?;
|
let diff_scope = artificial_diff_scope("mandatory value", child_status)?;
|
||||||
full_status.push(diff_scope);
|
full_status.push(diff_scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if full_status.is_empty() {
|
if full_status.is_empty() {
|
||||||
Ok(ComparePropertiesResult::NoChange)
|
Ok(ComparePropertiesResult::NoChange)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use super::compare_field::compare_property_list_of_list_of_list_of_ast_nodes;
|
||||||
use super::compare_field::compare_property_list_of_quoted_string;
|
use super::compare_field::compare_property_list_of_quoted_string;
|
||||||
use super::compare_field::compare_property_object_tree;
|
|
||||||
use super::compare_field::compare_property_optional_pair;
|
|
||||||
use super::compare_field::compare_property_quoted_string;
|
use super::compare_field::compare_property_quoted_string;
|
||||||
use super::compare_field::ComparePropertiesResult;
|
use super::compare_field::ComparePropertiesResult;
|
||||||
use super::diff::DiffEntry;
|
use super::diff::DiffEntry;
|
||||||
@ -377,23 +376,13 @@ where
|
|||||||
)?;
|
)?;
|
||||||
ret.push(diff);
|
ret.push(diff);
|
||||||
}
|
}
|
||||||
AffiliatedKeywordValue::OptionalPair { optval, val } => {
|
AffiliatedKeywordValue::ListOfListsOfObjects(rust_value) => {
|
||||||
let diff = compare_property_optional_pair(
|
let diff = compare_property_list_of_list_of_list_of_ast_nodes(
|
||||||
source,
|
source,
|
||||||
emacs,
|
emacs,
|
||||||
rust,
|
rust,
|
||||||
emacs_property_name.as_str(),
|
emacs_property_name.as_str(),
|
||||||
|_| Some((*optval, *val)),
|
|_| Some(rust_value),
|
||||||
)?;
|
|
||||||
ret.push(diff);
|
|
||||||
}
|
|
||||||
AffiliatedKeywordValue::ObjectTree(rust_value) => {
|
|
||||||
let diff = compare_property_object_tree(
|
|
||||||
source,
|
|
||||||
emacs,
|
|
||||||
rust,
|
|
||||||
emacs_property_name.as_str(),
|
|
||||||
|_| Some(rust_value.iter()),
|
|
||||||
)?;
|
)?;
|
||||||
ret.push(diff);
|
ret.push(diff);
|
||||||
}
|
}
|
||||||
|
@ -35,46 +35,28 @@ where
|
|||||||
let mut ret = BTreeMap::new();
|
let mut ret = BTreeMap::new();
|
||||||
for kw in input {
|
for kw in input {
|
||||||
let translated_name = translate_name(global_settings, kw.key);
|
let translated_name = translate_name(global_settings, kw.key);
|
||||||
let keyword_type = identify_keyword_type(global_settings, translated_name.as_str());
|
if is_single_string_keyword(global_settings, translated_name.as_str()) {
|
||||||
match keyword_type {
|
|
||||||
AffiliatedKeywordType::SingleString => {
|
|
||||||
ret.insert(
|
ret.insert(
|
||||||
translated_name,
|
translated_name,
|
||||||
AffiliatedKeywordValue::SingleString(kw.value),
|
AffiliatedKeywordValue::SingleString(kw.value),
|
||||||
);
|
);
|
||||||
}
|
} else if is_list_of_single_string_keyword(global_settings, translated_name.as_str()) {
|
||||||
AffiliatedKeywordType::ListOfStrings => {
|
let list_of_strings = ret
|
||||||
let list_of_strings = ret.entry(translated_name).or_insert_with(|| {
|
.entry(translated_name)
|
||||||
AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1))
|
.or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1)));
|
||||||
});
|
|
||||||
match list_of_strings {
|
match list_of_strings {
|
||||||
|
AffiliatedKeywordValue::ListOfStrings(list_of_strings)
|
||||||
|
if list_of_strings.is_empty() =>
|
||||||
|
{
|
||||||
|
list_of_strings.push(kw.value);
|
||||||
|
}
|
||||||
AffiliatedKeywordValue::ListOfStrings(list_of_strings) => {
|
AffiliatedKeywordValue::ListOfStrings(list_of_strings) => {
|
||||||
|
list_of_strings.clear();
|
||||||
list_of_strings.push(kw.value);
|
list_of_strings.push(kw.value);
|
||||||
}
|
}
|
||||||
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
||||||
}
|
}
|
||||||
}
|
} else if is_list_of_objects_keyword(global_settings, translated_name.as_str()) {
|
||||||
AffiliatedKeywordType::OptionalPair => {
|
|
||||||
let (_remaining, optional_string) = opt(all_consuming(map(
|
|
||||||
tuple((
|
|
||||||
take_until::<_, &str, nom::error::Error<_>>("["),
|
|
||||||
tag("["),
|
|
||||||
recognize(many_till(anychar, peek(tuple((tag("]"), eof))))),
|
|
||||||
tag("]"),
|
|
||||||
eof,
|
|
||||||
)),
|
|
||||||
|(_, _, objects, _, _)| objects,
|
|
||||||
)))(kw.key.into())
|
|
||||||
.expect("Parser should always succeed.");
|
|
||||||
ret.insert(
|
|
||||||
translated_name,
|
|
||||||
AffiliatedKeywordValue::OptionalPair {
|
|
||||||
optval: optional_string,
|
|
||||||
val: kw.value,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
AffiliatedKeywordType::ObjectTree => {
|
|
||||||
let initial_context = ContextElement::document_context();
|
let initial_context = ContextElement::document_context();
|
||||||
let initial_context = Context::new(global_settings, List::new(&initial_context));
|
let initial_context = Context::new(global_settings, List::new(&initial_context));
|
||||||
|
|
||||||
@ -98,23 +80,30 @@ where
|
|||||||
.expect("Object parser should always succeed.");
|
.expect("Object parser should always succeed.");
|
||||||
|
|
||||||
// TODO: This should be omitting footnote references
|
// TODO: This should be omitting footnote references
|
||||||
let (_remaining, objects) =
|
let (_remaining, objects) = all_consuming(many0(parser_with_context!(
|
||||||
all_consuming(many0(parser_with_context!(standard_set_object)(
|
standard_set_object
|
||||||
&initial_context,
|
)(&initial_context)))(kw.value.into())
|
||||||
)))(kw.value.into())
|
|
||||||
.expect("Object parser should always succeed.");
|
.expect("Object parser should always succeed.");
|
||||||
|
let list_of_lists = ret.entry(translated_name).or_insert_with(|| {
|
||||||
let entry_per_keyword_list = ret
|
AffiliatedKeywordValue::ListOfListsOfObjects(Vec::with_capacity(1))
|
||||||
|
});
|
||||||
|
match list_of_lists {
|
||||||
|
AffiliatedKeywordValue::ListOfListsOfObjects(list_of_lists) => {
|
||||||
|
list_of_lists.push((optional_objects, objects));
|
||||||
|
}
|
||||||
|
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let list_of_strings = ret
|
||||||
.entry(translated_name)
|
.entry(translated_name)
|
||||||
.or_insert_with(|| AffiliatedKeywordValue::ObjectTree(Vec::with_capacity(1)));
|
.or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1)));
|
||||||
match entry_per_keyword_list {
|
match list_of_strings {
|
||||||
AffiliatedKeywordValue::ObjectTree(entry_per_keyword_list) => {
|
AffiliatedKeywordValue::ListOfStrings(list_of_strings) => {
|
||||||
entry_per_keyword_list.push((optional_objects, objects));
|
list_of_strings.push(kw.value);
|
||||||
}
|
}
|
||||||
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
AffiliatedKeywords { keywords: ret }
|
AffiliatedKeywords { keywords: ret }
|
||||||
}
|
}
|
||||||
@ -132,37 +121,40 @@ fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s
|
|||||||
name_until_optval.to_lowercase()
|
name_until_optval.to_lowercase()
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AffiliatedKeywordType {
|
fn is_single_string_keyword<'g, 's>(
|
||||||
SingleString,
|
_global_settings: &'g GlobalSettings<'g, 's>,
|
||||||
ListOfStrings,
|
name: &'s str,
|
||||||
OptionalPair,
|
) -> bool {
|
||||||
ObjectTree,
|
// TODO: Is this defined by an elisp variable?
|
||||||
|
for single_string_name in ["plot", "name"] {
|
||||||
|
if name.eq_ignore_ascii_case(single_string_name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identify_keyword_type<'g, 's>(
|
fn is_list_of_single_string_keyword<'g, 's>(
|
||||||
|
_global_settings: &'g GlobalSettings<'g, 's>,
|
||||||
|
name: &'s str,
|
||||||
|
) -> bool {
|
||||||
|
// TODO: Is this defined by an elisp variable?
|
||||||
|
for single_string_name in ["results"] {
|
||||||
|
if name.eq_ignore_ascii_case(single_string_name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_list_of_objects_keyword<'g, 's>(
|
||||||
global_settings: &'g GlobalSettings<'g, 's>,
|
global_settings: &'g GlobalSettings<'g, 's>,
|
||||||
name: &'s str,
|
name: &'s str,
|
||||||
) -> AffiliatedKeywordType {
|
) -> bool {
|
||||||
let is_multiple = ["CAPTION", "HEADER"]
|
for parsed_keyword in global_settings.element_parsed_keywords {
|
||||||
.into_iter()
|
if name.eq_ignore_ascii_case(parsed_keyword) {
|
||||||
.any(|candidate| name.eq_ignore_ascii_case(candidate))
|
return true;
|
||||||
|| name.to_lowercase().starts_with("attr_");
|
|
||||||
let is_parsed = global_settings
|
|
||||||
.element_parsed_keywords
|
|
||||||
.iter()
|
|
||||||
.any(|candidate| name.eq_ignore_ascii_case(candidate));
|
|
||||||
let can_have_optval = global_settings
|
|
||||||
.element_dual_keywords
|
|
||||||
.iter()
|
|
||||||
.any(|candidate| name.eq_ignore_ascii_case(candidate));
|
|
||||||
match (is_multiple, is_parsed, can_have_optval) {
|
|
||||||
(true, true, true) => AffiliatedKeywordType::ObjectTree,
|
|
||||||
(true, true, false) => unreachable!("Nothing like this exists in upstream org-mode."),
|
|
||||||
(true, false, true) => unreachable!("Nothing like this exists in upstream org-mode."),
|
|
||||||
(true, false, false) => AffiliatedKeywordType::ListOfStrings,
|
|
||||||
(false, true, true) => unreachable!("Nothing like this exists in upstream org-mode."),
|
|
||||||
(false, true, false) => unreachable!("Nothing like this exists in upstream org-mode."),
|
|
||||||
(false, false, true) => AffiliatedKeywordType::OptionalPair,
|
|
||||||
(false, false, false) => AffiliatedKeywordType::SingleString,
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ use super::keyword::affiliated_keyword;
|
|||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use super::util::org_line_ending;
|
use super::util::org_line_ending;
|
||||||
use crate::context::parser_with_context;
|
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::Res;
|
use crate::error::Res;
|
||||||
use crate::parser::util::get_consumed;
|
use crate::parser::util::get_consumed;
|
||||||
@ -51,11 +50,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn detect_diary_sexp<'b, 'g, 'r, 's>(
|
pub(crate) fn detect_diary_sexp<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, ()> {
|
|
||||||
let (input, _) = many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
|
||||||
tuple((start_of_line, tag("%%(")))(input)?;
|
tuple((start_of_line, tag("%%(")))(input)?;
|
||||||
Ok((input, ()))
|
Ok((input, ()))
|
||||||
}
|
}
|
||||||
|
@ -56,8 +56,7 @@ fn _element<'b, 'g, 'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
can_be_paragraph: bool,
|
can_be_paragraph: bool,
|
||||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||||
let (post_affiliated_keywords_input, affiliated_keywords) =
|
let (post_affiliated_keywords_input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||||
many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
|
||||||
|
|
||||||
let mut affiliated_keywords = affiliated_keywords.into_iter();
|
let mut affiliated_keywords = affiliated_keywords.into_iter();
|
||||||
|
|
||||||
@ -273,14 +272,13 @@ fn _detect_element<'b, 'g, 'r, 's>(
|
|||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
can_be_paragraph: bool,
|
can_be_paragraph: bool,
|
||||||
) -> Res<OrgSource<'s>, ()> {
|
) -> Res<OrgSource<'s>, ()> {
|
||||||
// TODO: unify parsing of affiliated keywords like we did for the element parser.
|
|
||||||
if alt((
|
if alt((
|
||||||
parser_with_context!(detect_plain_list)(context),
|
parser_with_context!(detect_plain_list)(context),
|
||||||
parser_with_context!(detect_footnote_definition)(context),
|
detect_footnote_definition,
|
||||||
parser_with_context!(detect_diary_sexp)(context),
|
detect_diary_sexp,
|
||||||
detect_comment,
|
detect_comment,
|
||||||
parser_with_context!(detect_fixed_width_area)(context),
|
detect_fixed_width_area,
|
||||||
parser_with_context!(detect_table)(context),
|
detect_table,
|
||||||
))(input)
|
))(input)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
|
@ -90,11 +90,8 @@ fn fixed_width_area_line<'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 detect_fixed_width_area<'b, 'g, 'r, 's>(
|
pub(crate) fn detect_fixed_width_area<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, ()> {
|
|
||||||
let (input, _) = many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
|
||||||
tuple((
|
tuple((
|
||||||
start_of_line,
|
start_of_line,
|
||||||
space0,
|
space0,
|
||||||
|
@ -125,7 +125,7 @@ fn footnote_definition_end<'b, 'g, 'r, 's>(
|
|||||||
let (remaining, source) = alt((
|
let (remaining, source) = alt((
|
||||||
recognize(tuple((
|
recognize(tuple((
|
||||||
parser_with_context!(maybe_consume_trailing_whitespace)(context),
|
parser_with_context!(maybe_consume_trailing_whitespace)(context),
|
||||||
parser_with_context!(detect_footnote_definition)(context),
|
detect_footnote_definition,
|
||||||
))),
|
))),
|
||||||
recognize(tuple((
|
recognize(tuple((
|
||||||
start_of_line,
|
start_of_line,
|
||||||
@ -139,11 +139,8 @@ fn footnote_definition_end<'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 detect_footnote_definition<'b, 'g, 'r, 's>(
|
pub(crate) fn detect_footnote_definition<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, ()> {
|
|
||||||
let (input, _) = many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
|
||||||
tuple((start_of_line, tag_no_case("[fn:"), label, tag("]")))(input)?;
|
tuple((start_of_line, tag_no_case("[fn:"), label, tag("]")))(input)?;
|
||||||
Ok((input, ()))
|
Ok((input, ()))
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ use nom::character::complete::one_of;
|
|||||||
use nom::character::complete::space0;
|
use nom::character::complete::space0;
|
||||||
use nom::combinator::consumed;
|
use nom::combinator::consumed;
|
||||||
use nom::combinator::eof;
|
use nom::combinator::eof;
|
||||||
use nom::combinator::map;
|
|
||||||
use nom::combinator::not;
|
use nom::combinator::not;
|
||||||
use nom::combinator::peek;
|
use nom::combinator::peek;
|
||||||
use nom::combinator::recognize;
|
use nom::combinator::recognize;
|
||||||
@ -22,7 +21,7 @@ use super::org_source::BracketDepth;
|
|||||||
use super::org_source::OrgSource;
|
use super::org_source::OrgSource;
|
||||||
use super::util::get_consumed;
|
use super::util::get_consumed;
|
||||||
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||||
use crate::context::parser_with_context;
|
use crate::context::Matcher;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
@ -31,9 +30,15 @@ use crate::parser::util::start_of_line;
|
|||||||
use crate::types::AffiliatedKeywords;
|
use crate::types::AffiliatedKeywords;
|
||||||
use crate::types::Keyword;
|
use crate::types::Keyword;
|
||||||
|
|
||||||
pub(crate) fn filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>>(
|
const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
|
||||||
|
"caption", "data", "headers", "header", "label", "name", "plot", "resname", "results",
|
||||||
|
"result", "source", "srcname", "tblname",
|
||||||
|
];
|
||||||
|
const ORG_ELEMENT_DUAL_KEYWORDS: [&'static str; 2] = ["caption", "results"];
|
||||||
|
|
||||||
|
pub(crate) fn filtered_keyword<F: Matcher>(
|
||||||
key_parser: F,
|
key_parser: F,
|
||||||
) -> impl Fn(OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
|
) -> impl for<'s> Fn(OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||||
move |input| _filtered_keyword(&key_parser, input)
|
move |input| _filtered_keyword(&key_parser, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +46,7 @@ pub(crate) fn filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, Or
|
|||||||
feature = "tracing",
|
feature = "tracing",
|
||||||
tracing::instrument(ret, level = "debug", skip(key_parser))
|
tracing::instrument(ret, level = "debug", skip(key_parser))
|
||||||
)]
|
)]
|
||||||
fn _filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>>(
|
fn _filtered_keyword<'s, F: Matcher>(
|
||||||
key_parser: F,
|
key_parser: F,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||||
@ -108,11 +113,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn affiliated_keyword<'b, 'g, 'r, 's>(
|
pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
filtered_keyword(affiliated_key)(input)
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
|
||||||
filtered_keyword(parser_with_context!(affiliated_key)(context))(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@ -147,30 +149,18 @@ fn regular_keyword_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn affiliated_key<'b, 'g, 'r, 's>(
|
fn affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
alt((
|
alt((
|
||||||
parser_with_context!(dual_affiliated_key)(context),
|
recognize(tuple((dual_affiliated_key, tag("["), optval, tag("]")))),
|
||||||
parser_with_context!(plain_affiliated_key)(context),
|
plain_affiliated_key,
|
||||||
export_keyword,
|
export_keyword,
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn plain_affiliated_key<'b, 'g, 'r, 's>(
|
fn plain_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
for keyword in ORG_ELEMENT_AFFILIATED_KEYWORDS {
|
||||||
input: OrgSource<'s>,
|
let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input);
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
for keyword in context.get_global_settings().element_affiliated_keywords {
|
|
||||||
let result = map(
|
|
||||||
tuple((
|
|
||||||
tag_no_case::<_, _, CustomError<_>>(*keyword),
|
|
||||||
peek(tag(":")),
|
|
||||||
)),
|
|
||||||
|(key, _)| key,
|
|
||||||
)(input);
|
|
||||||
match result {
|
match result {
|
||||||
Ok((remaining, ent)) => {
|
Ok((remaining, ent)) => {
|
||||||
return Ok((remaining, ent));
|
return Ok((remaining, ent));
|
||||||
@ -185,18 +175,9 @@ fn plain_affiliated_key<'b, 'g, 'r, 's>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn dual_affiliated_key<'b, 'g, 'r, 's>(
|
fn dual_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
for keyword in ORG_ELEMENT_DUAL_KEYWORDS {
|
||||||
input: OrgSource<'s>,
|
let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input);
|
||||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
|
||||||
for keyword in context.get_global_settings().element_dual_keywords {
|
|
||||||
let result = recognize(tuple((
|
|
||||||
tag_no_case::<_, _, CustomError<_>>(*keyword),
|
|
||||||
tag("["),
|
|
||||||
optval,
|
|
||||||
tag("]"),
|
|
||||||
peek(tag(":")),
|
|
||||||
)))(input);
|
|
||||||
match result {
|
match result {
|
||||||
Ok((remaining, ent)) => {
|
Ok((remaining, ent)) => {
|
||||||
return Ok((remaining, ent));
|
return Ok((remaining, ent));
|
||||||
|
@ -59,7 +59,7 @@ pub(crate) fn detect_plain_list<'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>, ()> {
|
) -> Res<OrgSource<'s>, ()> {
|
||||||
let (input, _) = many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||||
if verify(
|
if verify(
|
||||||
tuple((
|
tuple((
|
||||||
start_of_line,
|
start_of_line,
|
||||||
|
@ -93,11 +93,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn detect_table<'b, 'g, 'r, 's>(
|
pub(crate) fn detect_table<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||||
context: RefContext<'b, 'g, 'r, 's>,
|
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||||
input: OrgSource<'s>,
|
|
||||||
) -> Res<OrgSource<'s>, ()> {
|
|
||||||
let (input, _) = many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
|
||||||
tuple((start_of_line, space0, tag("|")))(input)?;
|
tuple((start_of_line, space0, tag("|")))(input)?;
|
||||||
Ok((input, ()))
|
Ok((input, ()))
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,7 @@ use super::Object;
|
|||||||
pub enum AffiliatedKeywordValue<'s> {
|
pub enum AffiliatedKeywordValue<'s> {
|
||||||
SingleString(&'s str),
|
SingleString(&'s str),
|
||||||
ListOfStrings(Vec<&'s str>),
|
ListOfStrings(Vec<&'s str>),
|
||||||
OptionalPair {
|
ListOfListsOfObjects(Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>),
|
||||||
optval: Option<&'s str>,
|
|
||||||
val: &'s str,
|
|
||||||
},
|
|
||||||
ObjectTree(Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user