Compare commits
14 Commits
dea3721b1c
...
d2d0e9e5dd
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d2d0e9e5dd | ||
![]() |
c86d1000c0 | ||
![]() |
911634cb42 | ||
![]() |
0aa746fb1e | ||
![]() |
33800c4a88 | ||
![]() |
909ccadfa1 | ||
![]() |
e352deb989 | ||
![]() |
f5a6a26c43 | ||
![]() |
dd7184da54 | ||
![]() |
1168ddb1fe | ||
![]() |
77ab636e6a | ||
![]() |
f5dcacc79d | ||
![]() |
e7c3c7aab6 | ||
![]() |
7603b0a1cc |
42
notes/affiliated_keyword_investigation/analysis.org
Normal file
42
notes/affiliated_keyword_investigation/analysis.org
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
* 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
|
31
notes/affiliated_keyword_investigation/run_test.bash
Executable file
31
notes/affiliated_keyword_investigation/run_test.bash
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
#!/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"
|
25
notes/affiliated_keyword_investigation/test_document.org
Normal file
25
notes/affiliated_keyword_investigation/test_document.org
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# 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
|
@ -0,0 +1,16 @@
|
|||||||
|
#+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,7 +16,6 @@ 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;
|
||||||
|
|
||||||
@ -288,6 +287,107 @@ 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>,
|
||||||
@ -412,118 +512,133 @@ where
|
|||||||
Ok(ComparePropertiesResult::NoChange)
|
Ok(ComparePropertiesResult::NoChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Special compare used for affiliate keywords that are parsed as objects.
|
pub(crate) fn compare_property_object_tree<
|
||||||
///
|
|
||||||
/// 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,
|
||||||
RG: Fn(R) -> Option<&'b Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>>,
|
RV: std::fmt::Debug + 'b,
|
||||||
|
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>>
|
||||||
// TODO: Replace Object<'s> with generics. I hard-coded Object in to make lifetimes easier.
|
where
|
||||||
let rust_value = rust_value_getter(rust_node);
|
AstNode<'b, 's>: From<&'b RV>,
|
||||||
|
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 (value, rust_value) = match (value, rust_value) {
|
let rust_value = rust_value_getter(rust_node);
|
||||||
|
let (outer_emacs_list, outer_rust_list) = match (value, rust_value) {
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
return Ok(ComparePropertiesResult::NoChange);
|
return Ok(ComparePropertiesResult::NoChange);
|
||||||
}
|
}
|
||||||
(None, Some(_)) | (Some(_), None) => {
|
(None, rv @ Some(_)) | (Some(_), rv @ 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, rust_value
|
emacs_field, value, rv
|
||||||
));
|
));
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
}
|
}
|
||||||
(Some(value), Some(rust_value)) if value.len() != rust_value.len() => {
|
(Some(el), Some(rl)) if el.len() != rl.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, value, rust_value
|
emacs_field, el, rl
|
||||||
));
|
));
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
}
|
}
|
||||||
(Some(value), Some(rust_value)) => (value, rust_value),
|
(Some(el), Some(rl)) => (el, rl),
|
||||||
};
|
};
|
||||||
|
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(outer_rust_list.len());
|
||||||
|
|
||||||
let mut full_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
for (kw_e, kw_r) in outer_emacs_list.into_iter().zip(outer_rust_list) {
|
||||||
|
let kw_e = kw_e.as_list()?;
|
||||||
// Iterate the outer lists
|
let child_status_length = kw_r.1.len() + kw_r.0.as_ref().map(|opt| opt.len()).unwrap_or(0);
|
||||||
for (value, (rust_optional, rust_value)) in value.iter().zip(rust_value.iter()) {
|
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(child_status_length);
|
||||||
let mut middle_value = value.as_list()?.iter();
|
if let Some(or) = &kw_r.0 {
|
||||||
// First element of middle list is the mandatory value (the value past the colon).
|
// if optional value
|
||||||
let mandatory_value = middle_value.next();
|
let mut kw_e = kw_e.into_iter();
|
||||||
let mandatory_value = match mandatory_value {
|
// First element is a list representing the mandatory value.
|
||||||
Some(mandatory_value) => mandatory_value,
|
if let Some(val_e) = kw_e.next() {
|
||||||
None => {
|
let el = val_e.as_list()?;
|
||||||
|
if el.len() != kw_r.1.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 el.into_iter().zip(kw_r.1.iter()) {
|
||||||
|
child_status.push(compare_ast_node(source, e, r.into())?);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
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, rust_value
|
emacs_field, kw_e, kw_r
|
||||||
));
|
));
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
}
|
}
|
||||||
};
|
// Remaining elements are the optional value.
|
||||||
|
if kw_e.len() != or.len() {
|
||||||
// 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 this_status = DiffStatus::Bad;
|
||||||
let message = Some(format!(
|
let message = Some(format!(
|
||||||
"{} optional value length mismatch (emacs != rust) {} != {} | {:?}",
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
emacs_field,
|
emacs_field, kw_e, kw_r
|
||||||
middle_value.len(),
|
|
||||||
rust_optional.len(),
|
|
||||||
rust_optional
|
|
||||||
));
|
));
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
}
|
}
|
||||||
for (e, r) in middle_value.zip(rust_optional) {
|
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())?);
|
||||||
}
|
}
|
||||||
if !child_status.is_empty() {
|
} else {
|
||||||
let diff_scope = artificial_diff_scope("optional value", child_status)?;
|
// if no optional value
|
||||||
full_status.push(diff_scope);
|
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));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Compare mandatory value
|
let e = kw_e
|
||||||
let mut child_status: Vec<DiffEntry<'b, 's>> = Vec::with_capacity(rust_value.len());
|
.first()
|
||||||
let mandatory_value = mandatory_value.as_list()?;
|
.map(Token::as_list)
|
||||||
if rust_value.len() != mandatory_value.len() {
|
.map_or(Ok(None), |r| r.map(Some))?
|
||||||
let this_status = DiffStatus::Bad;
|
.expect("The above if-statement proves this will be Some.")
|
||||||
let message = Some(format!(
|
.iter();
|
||||||
"{} mandatory value length mismatch (emacs != rust) {} != {} | {:?}",
|
let r = kw_r.1.iter();
|
||||||
emacs_field,
|
|
||||||
mandatory_value.len(),
|
if e.len() != r.len() {
|
||||||
rust_value.len(),
|
let this_status = DiffStatus::Bad;
|
||||||
rust_value
|
let message = Some(format!(
|
||||||
));
|
"{} mismatch (emacs != rust) {:?} != {:?}",
|
||||||
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
emacs_field, kw_e, kw_r
|
||||||
}
|
));
|
||||||
for (e, r) in mandatory_value.iter().zip(rust_value) {
|
return Ok(ComparePropertiesResult::SelfChange(this_status, message));
|
||||||
child_status.push(compare_ast_node(source, e, r.into())?);
|
}
|
||||||
|
|
||||||
|
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,7 +1,8 @@
|
|||||||
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;
|
||||||
@ -376,13 +377,23 @@ where
|
|||||||
)?;
|
)?;
|
||||||
ret.push(diff);
|
ret.push(diff);
|
||||||
}
|
}
|
||||||
AffiliatedKeywordValue::ListOfListsOfObjects(rust_value) => {
|
AffiliatedKeywordValue::OptionalPair { optval, val } => {
|
||||||
let diff = compare_property_list_of_list_of_list_of_ast_nodes(
|
let diff = compare_property_optional_pair(
|
||||||
source,
|
source,
|
||||||
emacs,
|
emacs,
|
||||||
rust,
|
rust,
|
||||||
emacs_property_name.as_str(),
|
emacs_property_name.as_str(),
|
||||||
|_| Some(rust_value),
|
|_| Some((*optval, *val)),
|
||||||
|
)?;
|
||||||
|
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,75 +35,86 @@ 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);
|
||||||
if is_single_string_keyword(global_settings, translated_name.as_str()) {
|
let keyword_type = identify_keyword_type(global_settings, translated_name.as_str());
|
||||||
ret.insert(
|
match keyword_type {
|
||||||
translated_name,
|
AffiliatedKeywordType::SingleString => {
|
||||||
AffiliatedKeywordValue::SingleString(kw.value),
|
ret.insert(
|
||||||
);
|
translated_name,
|
||||||
} else if is_list_of_single_string_keyword(global_settings, translated_name.as_str()) {
|
AffiliatedKeywordValue::SingleString(kw.value),
|
||||||
let list_of_strings = ret
|
);
|
||||||
.entry(translated_name)
|
|
||||||
.or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1)));
|
|
||||||
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) => {
|
|
||||||
list_of_strings.clear();
|
|
||||||
list_of_strings.push(kw.value);
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
|
||||||
}
|
}
|
||||||
} else if is_list_of_objects_keyword(global_settings, translated_name.as_str()) {
|
AffiliatedKeywordType::ListOfStrings => {
|
||||||
let initial_context = ContextElement::document_context();
|
let list_of_strings = ret.entry(translated_name).or_insert_with(|| {
|
||||||
let initial_context = Context::new(global_settings, List::new(&initial_context));
|
AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1))
|
||||||
|
});
|
||||||
let (_remaining, optional_objects) = opt(all_consuming(map(
|
match list_of_strings {
|
||||||
tuple((
|
AffiliatedKeywordValue::ListOfStrings(list_of_strings) => {
|
||||||
take_until("["),
|
list_of_strings.push(kw.value);
|
||||||
tag("["),
|
}
|
||||||
map_parser(
|
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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))))),
|
recognize(many_till(anychar, peek(tuple((tag("]"), eof))))),
|
||||||
confine_context(|i| {
|
tag("]"),
|
||||||
all_consuming(many0(parser_with_context!(standard_set_object)(
|
eof,
|
||||||
&initial_context,
|
)),
|
||||||
)))(i)
|
|(_, _, objects, _, _)| objects,
|
||||||
}),
|
)))(kw.key.into())
|
||||||
),
|
.expect("Parser should always succeed.");
|
||||||
tag("]"),
|
ret.insert(
|
||||||
eof,
|
translated_name,
|
||||||
)),
|
AffiliatedKeywordValue::OptionalPair {
|
||||||
|(_, _, objects, _, _)| objects,
|
optval: optional_string,
|
||||||
)))(kw.key.into())
|
val: kw.value,
|
||||||
.expect("Object parser should always succeed.");
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
AffiliatedKeywordType::ObjectTree => {
|
||||||
|
let initial_context = ContextElement::document_context();
|
||||||
|
let initial_context = Context::new(global_settings, List::new(&initial_context));
|
||||||
|
|
||||||
// TODO: This should be omitting footnote references
|
let (_remaining, optional_objects) = opt(all_consuming(map(
|
||||||
let (_remaining, objects) = all_consuming(many0(parser_with_context!(
|
tuple((
|
||||||
standard_set_object
|
take_until("["),
|
||||||
)(&initial_context)))(kw.value.into())
|
tag("["),
|
||||||
.expect("Object parser should always succeed.");
|
map_parser(
|
||||||
let list_of_lists = ret.entry(translated_name).or_insert_with(|| {
|
recognize(many_till(anychar, peek(tuple((tag("]"), eof))))),
|
||||||
AffiliatedKeywordValue::ListOfListsOfObjects(Vec::with_capacity(1))
|
confine_context(|i| {
|
||||||
});
|
all_consuming(many0(parser_with_context!(standard_set_object)(
|
||||||
match list_of_lists {
|
&initial_context,
|
||||||
AffiliatedKeywordValue::ListOfListsOfObjects(list_of_lists) => {
|
)))(i)
|
||||||
list_of_lists.push((optional_objects, objects));
|
}),
|
||||||
|
),
|
||||||
|
tag("]"),
|
||||||
|
eof,
|
||||||
|
)),
|
||||||
|
|(_, _, objects, _, _)| objects,
|
||||||
|
)))(kw.key.into())
|
||||||
|
.expect("Object parser should always succeed.");
|
||||||
|
|
||||||
|
// TODO: This should be omitting footnote references
|
||||||
|
let (_remaining, objects) =
|
||||||
|
all_consuming(many0(parser_with_context!(standard_set_object)(
|
||||||
|
&initial_context,
|
||||||
|
)))(kw.value.into())
|
||||||
|
.expect("Object parser should always succeed.");
|
||||||
|
|
||||||
|
let entry_per_keyword_list = ret
|
||||||
|
.entry(translated_name)
|
||||||
|
.or_insert_with(|| AffiliatedKeywordValue::ObjectTree(Vec::with_capacity(1)));
|
||||||
|
match entry_per_keyword_list {
|
||||||
|
AffiliatedKeywordValue::ObjectTree(entry_per_keyword_list) => {
|
||||||
|
entry_per_keyword_list.push((optional_objects, objects));
|
||||||
|
}
|
||||||
|
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
||||||
}
|
}
|
||||||
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
|
||||||
}
|
}
|
||||||
} else {
|
};
|
||||||
let list_of_strings = ret
|
|
||||||
.entry(translated_name)
|
|
||||||
.or_insert_with(|| AffiliatedKeywordValue::ListOfStrings(Vec::with_capacity(1)));
|
|
||||||
match list_of_strings {
|
|
||||||
AffiliatedKeywordValue::ListOfStrings(list_of_strings) => {
|
|
||||||
list_of_strings.push(kw.value);
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid AffiliatedKeywordValue type."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
AffiliatedKeywords { keywords: ret }
|
AffiliatedKeywords { keywords: ret }
|
||||||
}
|
}
|
||||||
@ -121,40 +132,37 @@ fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s
|
|||||||
name_until_optval.to_lowercase()
|
name_until_optval.to_lowercase()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_single_string_keyword<'g, 's>(
|
enum AffiliatedKeywordType {
|
||||||
_global_settings: &'g GlobalSettings<'g, 's>,
|
SingleString,
|
||||||
name: &'s str,
|
ListOfStrings,
|
||||||
) -> bool {
|
OptionalPair,
|
||||||
// TODO: Is this defined by an elisp variable?
|
ObjectTree,
|
||||||
for single_string_name in ["plot", "name"] {
|
|
||||||
if name.eq_ignore_ascii_case(single_string_name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_list_of_single_string_keyword<'g, 's>(
|
fn identify_keyword_type<'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,
|
||||||
) -> bool {
|
) -> AffiliatedKeywordType {
|
||||||
for parsed_keyword in global_settings.element_parsed_keywords {
|
let is_multiple = ["CAPTION", "HEADER"]
|
||||||
if name.eq_ignore_ascii_case(parsed_keyword) {
|
.into_iter()
|
||||||
return true;
|
.any(|candidate| name.eq_ignore_ascii_case(candidate))
|
||||||
}
|
|| 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,6 +9,7 @@ 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;
|
||||||
@ -50,8 +51,11 @@ 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<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
pub(crate) fn detect_diary_sexp<'b, 'g, 'r, 's>(
|
||||||
let (input, _) = many0(affiliated_keyword)(input)?;
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
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,7 +56,8 @@ 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) = many0(affiliated_keyword)(input)?;
|
let (post_affiliated_keywords_input, affiliated_keywords) =
|
||||||
|
many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
||||||
|
|
||||||
let mut affiliated_keywords = affiliated_keywords.into_iter();
|
let mut affiliated_keywords = affiliated_keywords.into_iter();
|
||||||
|
|
||||||
@ -272,13 +273,14 @@ 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),
|
||||||
detect_footnote_definition,
|
parser_with_context!(detect_footnote_definition)(context),
|
||||||
detect_diary_sexp,
|
parser_with_context!(detect_diary_sexp)(context),
|
||||||
detect_comment,
|
detect_comment,
|
||||||
detect_fixed_width_area,
|
parser_with_context!(detect_fixed_width_area)(context),
|
||||||
detect_table,
|
parser_with_context!(detect_table)(context),
|
||||||
))(input)
|
))(input)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
|
@ -90,8 +90,11 @@ 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<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
pub(crate) fn detect_fixed_width_area<'b, 'g, 'r, 's>(
|
||||||
let (input, _) = many0(affiliated_keyword)(input)?;
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
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),
|
||||||
detect_footnote_definition,
|
parser_with_context!(detect_footnote_definition)(context),
|
||||||
))),
|
))),
|
||||||
recognize(tuple((
|
recognize(tuple((
|
||||||
start_of_line,
|
start_of_line,
|
||||||
@ -139,8 +139,11 @@ 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<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
pub(crate) fn detect_footnote_definition<'b, 'g, 'r, 's>(
|
||||||
let (input, _) = many0(affiliated_keyword)(input)?;
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
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,6 +9,7 @@ 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;
|
||||||
@ -21,7 +22,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::Matcher;
|
use crate::context::parser_with_context;
|
||||||
use crate::context::RefContext;
|
use crate::context::RefContext;
|
||||||
use crate::error::CustomError;
|
use crate::error::CustomError;
|
||||||
use crate::error::MyError;
|
use crate::error::MyError;
|
||||||
@ -30,15 +31,9 @@ use crate::parser::util::start_of_line;
|
|||||||
use crate::types::AffiliatedKeywords;
|
use crate::types::AffiliatedKeywords;
|
||||||
use crate::types::Keyword;
|
use crate::types::Keyword;
|
||||||
|
|
||||||
const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
|
pub(crate) fn filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>>(
|
||||||
"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 for<'s> Fn(OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
|
) -> impl Fn(OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||||
move |input| _filtered_keyword(&key_parser, input)
|
move |input| _filtered_keyword(&key_parser, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +41,7 @@ pub(crate) fn filtered_keyword<F: Matcher>(
|
|||||||
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: Matcher>(
|
fn _filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>>>(
|
||||||
key_parser: F,
|
key_parser: F,
|
||||||
input: OrgSource<'s>,
|
input: OrgSource<'s>,
|
||||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||||
@ -113,8 +108,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
|
pub(crate) fn affiliated_keyword<'b, 'g, 'r, 's>(
|
||||||
filtered_keyword(affiliated_key)(input)
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||||
|
filtered_keyword(parser_with_context!(affiliated_key)(context))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@ -149,18 +147,30 @@ 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<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
fn affiliated_key<'b, 'g, 'r, 's>(
|
||||||
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
input: OrgSource<'s>,
|
||||||
|
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||||
alt((
|
alt((
|
||||||
recognize(tuple((dual_affiliated_key, tag("["), optval, tag("]")))),
|
parser_with_context!(dual_affiliated_key)(context),
|
||||||
plain_affiliated_key,
|
parser_with_context!(plain_affiliated_key)(context),
|
||||||
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<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
fn plain_affiliated_key<'b, 'g, 'r, 's>(
|
||||||
for keyword in ORG_ELEMENT_AFFILIATED_KEYWORDS {
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input);
|
input: OrgSource<'s>,
|
||||||
|
) -> 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));
|
||||||
@ -175,9 +185,18 @@ fn plain_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSourc
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
fn dual_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
fn dual_affiliated_key<'b, 'g, 'r, 's>(
|
||||||
for keyword in ORG_ELEMENT_DUAL_KEYWORDS {
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
let result = tag_no_case::<_, _, CustomError<_>>(keyword)(input);
|
input: OrgSource<'s>,
|
||||||
|
) -> 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(affiliated_keyword)(input)?;
|
let (input, _) = many0(parser_with_context!(affiliated_keyword)(context))(input)?;
|
||||||
if verify(
|
if verify(
|
||||||
tuple((
|
tuple((
|
||||||
start_of_line,
|
start_of_line,
|
||||||
|
@ -93,8 +93,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||||
pub(crate) fn detect_table<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
pub(crate) fn detect_table<'b, 'g, 'r, 's>(
|
||||||
let (input, _) = many0(affiliated_keyword)(input)?;
|
context: RefContext<'b, 'g, 'r, 's>,
|
||||||
|
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,7 +6,11 @@ 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>),
|
||||||
ListOfListsOfObjects(Vec<(Option<Vec<Object<'s>>>, Vec<Object<'s>>)>),
|
OptionalPair {
|
||||||
|
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