Merge branch 'lesser_block_properties'
This commit is contained in:
commit
02af3d0081
7
Makefile
7
Makefile
@ -46,6 +46,13 @@ dockertest:
|
||||
> $(MAKE) -C docker/organic_test
|
||||
> docker run --init --rm -i -t --read-only -v "$$(readlink -f ./):/source:ro" --mount type=tmpfs,destination=/tmp --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test --no-default-features --features compare --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
|
||||
|
||||
.PHONY: buildtest
|
||||
buildtest:
|
||||
> cargo build --no-default-features
|
||||
> cargo build --no-default-features --features compare
|
||||
> cargo build --no-default-features --features tracing
|
||||
> cargo build --no-default-features --features compare,tracing
|
||||
|
||||
.PHONY: foreign_document_test
|
||||
foreign_document_test:
|
||||
> $(MAKE) -C docker/organic_test run_foreign_document_test
|
||||
|
17
org_mode_samples/affiliated_keyword/all_name.org
Normal file
17
org_mode_samples/affiliated_keyword/all_name.org
Normal file
@ -0,0 +1,17 @@
|
||||
#+name: foo
|
||||
: bar
|
||||
|
||||
#+source: foo
|
||||
: bar
|
||||
|
||||
#+tblname: foo
|
||||
: bar
|
||||
|
||||
#+resname: foo
|
||||
: bar
|
||||
|
||||
#+srcname: foo
|
||||
: bar
|
||||
|
||||
#+label: foo
|
||||
: bar
|
8
org_mode_samples/affiliated_keyword/case_insensitive.org
Normal file
8
org_mode_samples/affiliated_keyword/case_insensitive.org
Normal file
@ -0,0 +1,8 @@
|
||||
#+NAME: foo
|
||||
bar
|
||||
|
||||
#+NaMe: baz
|
||||
cat
|
||||
|
||||
#+name: lorem
|
||||
ipsum
|
6
org_mode_samples/affiliated_keyword/multiple_name.org
Normal file
6
org_mode_samples/affiliated_keyword/multiple_name.org
Normal file
@ -0,0 +1,6 @@
|
||||
#+name: foo
|
||||
#+source: bar
|
||||
#+name: baz
|
||||
#+tblname: lorem
|
||||
#+label: ipsum
|
||||
: dolar
|
@ -0,0 +1,2 @@
|
||||
#+tblname: foo
|
||||
: bar
|
10
org_mode_samples/greater_element/drawer/name.org
Normal file
10
org_mode_samples/greater_element/drawer/name.org
Normal file
@ -0,0 +1,10 @@
|
||||
* Headline
|
||||
before
|
||||
#+NAME: foo
|
||||
:candle:
|
||||
inside
|
||||
|
||||
the drawer
|
||||
:end:
|
||||
|
||||
after
|
7
org_mode_samples/greater_element/dynamic_block/name.org
Normal file
7
org_mode_samples/greater_element/dynamic_block/name.org
Normal file
@ -0,0 +1,7 @@
|
||||
#+NAME: foo
|
||||
#+BEGIN: clocktable :scope file :maxlevel 2
|
||||
#+CAPTION: Clock summary at [2023-08-25 Fri 05:34]
|
||||
| Headline | Time |
|
||||
|--------------+--------|
|
||||
| *Total time* | *0:00* |
|
||||
#+END:
|
@ -0,0 +1,8 @@
|
||||
#+begin_center
|
||||
|
||||
#+end_center
|
||||
|
||||
#+begin_center
|
||||
#+NAME: foo
|
||||
|
||||
#+end_center
|
@ -0,0 +1,4 @@
|
||||
#+NAME: foo
|
||||
#+begin_center
|
||||
|
||||
#+end_center
|
@ -0,0 +1,4 @@
|
||||
#+NAME: foo
|
||||
#+begin_quote
|
||||
|
||||
#+end_quote
|
@ -0,0 +1,6 @@
|
||||
#+NAME: foo
|
||||
#+begin_defun
|
||||
foo
|
||||
|
||||
{{{bar(baz)}}}
|
||||
#+end_defun
|
2
org_mode_samples/greater_element/plain_list/name.org
Normal file
2
org_mode_samples/greater_element/plain_list/name.org
Normal file
@ -0,0 +1,2 @@
|
||||
#+NAME: foo
|
||||
1. bar
|
2
org_mode_samples/greater_element/table/name.org
Normal file
2
org_mode_samples/greater_element/table/name.org
Normal file
@ -0,0 +1,2 @@
|
||||
#+NAME: foo
|
||||
| foo | bar |
|
2
org_mode_samples/lesser_element/babel_call/name.org
Normal file
2
org_mode_samples/lesser_element/babel_call/name.org
Normal file
@ -0,0 +1,2 @@
|
||||
#+NAME: foo
|
||||
#+call: foo(bar="baz")
|
3
org_mode_samples/lesser_element/clock/name.org
Normal file
3
org_mode_samples/lesser_element/clock/name.org
Normal file
@ -0,0 +1,3 @@
|
||||
CLOCK: [2023-04-21 Fri 19:32]--[2023-04-21 Fri 19:35] => 0:03
|
||||
#+NAME: foo
|
||||
CLOCK: [2023-04-21 Fri 19:43]
|
2
org_mode_samples/lesser_element/comment/name.org
Normal file
2
org_mode_samples/lesser_element/comment/name.org
Normal file
@ -0,0 +1,2 @@
|
||||
#+NAME: foo
|
||||
# Comments cannot have affiliated keywords.
|
2
org_mode_samples/lesser_element/diary_sexp/name.org
Normal file
2
org_mode_samples/lesser_element/diary_sexp/name.org
Normal file
@ -0,0 +1,2 @@
|
||||
#+NAME: foo
|
||||
%%(foo)
|
@ -0,0 +1,2 @@
|
||||
#+NAME: foo
|
||||
: bar
|
2
org_mode_samples/lesser_element/horizontal_rule/name.org
Normal file
2
org_mode_samples/lesser_element/horizontal_rule/name.org
Normal file
@ -0,0 +1,2 @@
|
||||
#+NAME: foo
|
||||
-----
|
2
org_mode_samples/lesser_element/keyword/name.org
Normal file
2
org_mode_samples/lesser_element/keyword/name.org
Normal file
@ -0,0 +1,2 @@
|
||||
#+NAME: foo
|
||||
#+FOO: BAR
|
@ -0,0 +1,4 @@
|
||||
#+NAME: foo
|
||||
\begin{foo}
|
||||
bar
|
||||
\end{foo}
|
@ -0,0 +1,10 @@
|
||||
#+begin_comment
|
||||
,* foo
|
||||
,,,** bar
|
||||
,*** baz
|
||||
lorem
|
||||
, ipsum
|
||||
,#+begin_src dolar
|
||||
|
||||
,#+end_src
|
||||
#+end_comment
|
@ -0,0 +1,4 @@
|
||||
#+NAME: foo
|
||||
#+begin_comment text
|
||||
bar
|
||||
#+end_comment
|
@ -0,0 +1,4 @@
|
||||
#+begin_comment
|
||||
This is a comment
|
||||
,* with an escaped line.
|
||||
#+end_comment
|
@ -0,0 +1,3 @@
|
||||
#+begin_comment -n 20
|
||||
foo
|
||||
#+end_comment
|
@ -0,0 +1,10 @@
|
||||
#+begin_example
|
||||
,* foo
|
||||
,,,** bar
|
||||
,*** baz
|
||||
lorem
|
||||
, ipsum
|
||||
,#+begin_src dolar
|
||||
|
||||
,#+end_src
|
||||
#+end_example
|
@ -0,0 +1,7 @@
|
||||
#+begin_example python :exports results
|
||||
print("foo")
|
||||
#+end_example
|
||||
|
||||
#+begin_example python -n :exports results
|
||||
print("foo")
|
||||
#+end_example
|
@ -0,0 +1,3 @@
|
||||
#+begin_example elisp -n 5
|
||||
foo
|
||||
#+end_example
|
@ -0,0 +1,7 @@
|
||||
#+begin_example -n -10
|
||||
foo
|
||||
#+end_example
|
||||
|
||||
#+begin_example +n -15
|
||||
bar
|
||||
#+end_example
|
@ -0,0 +1,7 @@
|
||||
#+begin_example -n 0
|
||||
foo
|
||||
#+end_example
|
||||
|
||||
#+begin_example +n 0
|
||||
bar
|
||||
#+end_example
|
@ -0,0 +1,8 @@
|
||||
#+begin_example -n 5
|
||||
foo
|
||||
#+end_example
|
||||
|
||||
# Line numbering starts at 15 for the example below since it uses +n.
|
||||
#+begin_example +n 10
|
||||
bar
|
||||
#+end_example
|
@ -0,0 +1,4 @@
|
||||
#+NAME: foo
|
||||
#+begin_example text
|
||||
bar
|
||||
#+end_example
|
@ -0,0 +1,3 @@
|
||||
#+begin_example foo -k
|
||||
bar
|
||||
#+end_example
|
@ -0,0 +1,3 @@
|
||||
#+begin_example foo -n bar -k baz
|
||||
|
||||
#+end_example
|
@ -0,0 +1,15 @@
|
||||
#+begin_example text -i
|
||||
foo
|
||||
#+end_example
|
||||
|
||||
#+begin_example text -n -i
|
||||
foo
|
||||
#+end_example
|
||||
|
||||
#+begin_example text
|
||||
foo
|
||||
#+end_example
|
||||
|
||||
#+begin_example text -n -r -k
|
||||
foo
|
||||
#+end_example
|
@ -0,0 +1,6 @@
|
||||
#+begin_example text
|
||||
foo
|
||||
bar (ref:here)
|
||||
baz
|
||||
#+end_example
|
||||
Link to the reference: [[(here)]]
|
@ -0,0 +1,2 @@
|
||||
#+begin_example
|
||||
#+end_example
|
@ -0,0 +1,15 @@
|
||||
#+BEGIN_EXAMPLE elisp -n -r -l "((%s))"
|
||||
foo
|
||||
#+END_EXAMPLE
|
||||
|
||||
#+BEGIN_EXAMPLE elisp -k -n -r -l "((%s))"
|
||||
foo
|
||||
#+END_EXAMPLE
|
||||
|
||||
#+BEGIN_EXAMPLE elisp -k 8 -n -r -l "((%s))"
|
||||
foo
|
||||
#+END_EXAMPLE
|
||||
|
||||
#+BEGIN_EXAMPLE elisp -n -r -k -l "((%s))"
|
||||
foo
|
||||
#+END_EXAMPLE
|
@ -0,0 +1,3 @@
|
||||
#+begin_example +n 10
|
||||
foo
|
||||
#+end_example
|
@ -0,0 +1,10 @@
|
||||
#+begin_export html
|
||||
,* foo
|
||||
,,,** bar
|
||||
,*** baz
|
||||
lorem
|
||||
, ipsum
|
||||
,#+begin_src dolar
|
||||
|
||||
,#+end_src
|
||||
#+end_export
|
@ -0,0 +1,4 @@
|
||||
#+NAME: foo
|
||||
#+begin_export text
|
||||
bar
|
||||
#+end_export
|
@ -0,0 +1,3 @@
|
||||
#+begin_export latex
|
||||
This would be LaTeX code.
|
||||
#+end_export
|
6
org_mode_samples/lesser_element/lesser_block/nested.org
Normal file
6
org_mode_samples/lesser_element/lesser_block/nested.org
Normal file
@ -0,0 +1,6 @@
|
||||
# Verse blocks are the only lesser blocks that contain objects
|
||||
#+begin_verse
|
||||
#+begin_comment
|
||||
This is a comment.
|
||||
#+end_comment
|
||||
#+end_verse
|
@ -0,0 +1,7 @@
|
||||
#+begin_src python :exports results
|
||||
print("foo")
|
||||
#+end_src
|
||||
|
||||
#+begin_src python -n :exports results
|
||||
print("foo")
|
||||
#+end_src
|
@ -0,0 +1,4 @@
|
||||
#+NAME: foo
|
||||
#+begin_src text
|
||||
bar
|
||||
#+end_src
|
@ -0,0 +1,7 @@
|
||||
#+begin_src python :exports results
|
||||
print("foo")
|
||||
#+end_src
|
||||
|
||||
#+begin_src python -n :exports results
|
||||
print("foo")
|
||||
#+end_src
|
@ -0,0 +1,3 @@
|
||||
#+begin_src foo -n bar -k baz
|
||||
|
||||
#+end_src
|
@ -0,0 +1,15 @@
|
||||
#+begin_src text -i
|
||||
foo
|
||||
#+end_src
|
||||
|
||||
#+begin_src text -n -i
|
||||
foo
|
||||
#+end_src
|
||||
|
||||
#+begin_src text
|
||||
foo
|
||||
#+end_src
|
||||
|
||||
#+begin_src text -n -r -k
|
||||
foo
|
||||
#+end_src
|
@ -0,0 +1,6 @@
|
||||
#+begin_src text
|
||||
foo
|
||||
bar (ref:here)
|
||||
baz
|
||||
#+end_src
|
||||
Link to the reference: [[(here)]]
|
@ -0,0 +1,4 @@
|
||||
#+NAME: foo
|
||||
#+begin_verse text
|
||||
bar
|
||||
#+end_verse
|
2
org_mode_samples/lesser_element/paragraph/name.org
Normal file
2
org_mode_samples/lesser_element/paragraph/name.org
Normal file
@ -0,0 +1,2 @@
|
||||
#+NAME: foo
|
||||
bar
|
@ -1,6 +1,7 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::compare::diff::compare_document;
|
||||
use crate::compare::diff::DiffResult;
|
||||
use crate::compare::parse::emacs_parse_anonymous_org_document;
|
||||
use crate::compare::parse::emacs_parse_file_org_document;
|
||||
use crate::compare::parse::get_emacs_version;
|
||||
@ -43,6 +44,12 @@ pub fn run_anonymous_compare_with_settings<P: AsRef<str>>(
|
||||
|
||||
if diff_result.is_bad() {
|
||||
Err("Diff results do not match.")?;
|
||||
} else {
|
||||
println!(
|
||||
"{color}Entire document passes.{reset}",
|
||||
color = DiffResult::foreground_color(0, 255, 0),
|
||||
reset = DiffResult::reset_color(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -83,6 +90,12 @@ pub fn run_compare_on_file_with_settings<P: AsRef<Path>>(
|
||||
|
||||
if diff_result.is_bad() {
|
||||
Err("Diff results do not match.")?;
|
||||
} else {
|
||||
println!(
|
||||
"{color}Entire document passes.{reset}",
|
||||
color = DiffResult::foreground_color(0, 255, 0),
|
||||
reset = DiffResult::reset_color(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -18,6 +18,7 @@ use crate::types::AstNode;
|
||||
use crate::types::BabelCall;
|
||||
use crate::types::Bold;
|
||||
use crate::types::CenterBlock;
|
||||
use crate::types::CharOffsetInLine;
|
||||
use crate::types::CheckboxType;
|
||||
use crate::types::Citation;
|
||||
use crate::types::CitationReference;
|
||||
@ -52,6 +53,7 @@ use crate::types::Keyword;
|
||||
use crate::types::LatexEnvironment;
|
||||
use crate::types::LatexFragment;
|
||||
use crate::types::LineBreak;
|
||||
use crate::types::LineNumber;
|
||||
use crate::types::Minute;
|
||||
use crate::types::MinuteInner;
|
||||
use crate::types::Month;
|
||||
@ -75,6 +77,7 @@ use crate::types::RadioTarget;
|
||||
use crate::types::RegularLink;
|
||||
use crate::types::RepeaterType;
|
||||
use crate::types::RepeaterWarningDelayValueType;
|
||||
use crate::types::RetainLabels;
|
||||
use crate::types::Section;
|
||||
use crate::types::SpecialBlock;
|
||||
use crate::types::SrcBlock;
|
||||
@ -83,6 +86,7 @@ use crate::types::StatisticsCookie;
|
||||
use crate::types::StrikeThrough;
|
||||
use crate::types::Subscript;
|
||||
use crate::types::Superscript;
|
||||
use crate::types::SwitchNumberLines;
|
||||
use crate::types::Table;
|
||||
use crate::types::TableCell;
|
||||
use crate::types::TableRow;
|
||||
@ -231,7 +235,7 @@ impl<'b, 's> DiffResult<'b, 's> {
|
||||
.any(|child| child.is_immediately_bad() || child.has_bad_children())
|
||||
}
|
||||
|
||||
fn foreground_color(red: u8, green: u8, blue: u8) -> String {
|
||||
pub(crate) fn foreground_color(red: u8, green: u8, blue: u8) -> String {
|
||||
if DiffResult::should_use_color() {
|
||||
format!(
|
||||
"\x1b[38;2;{red};{green};{blue}m",
|
||||
@ -245,7 +249,7 @@ impl<'b, 's> DiffResult<'b, 's> {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn background_color(red: u8, green: u8, blue: u8) -> String {
|
||||
pub(crate) fn background_color(red: u8, green: u8, blue: u8) -> String {
|
||||
if DiffResult::should_use_color() {
|
||||
format!(
|
||||
"\x1b[48;2;{red};{green};{blue}m",
|
||||
@ -258,7 +262,7 @@ impl<'b, 's> DiffResult<'b, 's> {
|
||||
}
|
||||
}
|
||||
|
||||
fn reset_color() -> &'static str {
|
||||
pub(crate) fn reset_color() -> &'static str {
|
||||
if DiffResult::should_use_color() {
|
||||
"\x1b[0m"
|
||||
} else {
|
||||
@ -837,8 +841,19 @@ fn compare_paragraph<'b, 's>(
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let mut child_status = Vec::new();
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
||||
@ -865,6 +880,17 @@ fn compare_plain_list<'b, 's>(
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
// Compare type
|
||||
// :type is an unquoted atom of either descriptive, ordered, or unordered
|
||||
let list_type = get_property_unquoted_atom(emacs, ":type")?;
|
||||
@ -1023,8 +1049,19 @@ fn compare_center_block<'b, 's>(
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let mut child_status = Vec::new();
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
||||
@ -1048,8 +1085,19 @@ fn compare_quote_block<'b, 's>(
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let mut child_status = Vec::new();
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
||||
@ -1076,14 +1124,25 @@ fn compare_special_block<'b, 's>(
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// Compare type
|
||||
let special_block_type =
|
||||
get_property_quoted_string(emacs, ":type")?.ok_or("Special blocks should have a name.")?;
|
||||
if special_block_type != rust.name {
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
special_block_type, rust.name
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
// Compare type
|
||||
let special_block_type =
|
||||
get_property_quoted_string(emacs, ":type")?.ok_or("Special blocks should have a name.")?;
|
||||
if special_block_type != rust.block_type {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
special_block_type, rust.block_type
|
||||
));
|
||||
}
|
||||
|
||||
@ -1126,14 +1185,25 @@ fn compare_dynamic_block<'b, 's>(
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// Compare block-name
|
||||
let block_name = get_property_quoted_string(emacs, ":block-name")?
|
||||
.ok_or("Dynamic blocks should have a name.")?;
|
||||
if block_name != rust.name {
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
block_name, rust.name
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
// Compare block-name
|
||||
let block_name = get_property_quoted_string(emacs, ":block-name")?
|
||||
.ok_or("Dynamic blocks should have a name.")?;
|
||||
if block_name != rust.block_name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
block_name, rust.block_name
|
||||
));
|
||||
}
|
||||
|
||||
@ -1177,6 +1247,17 @@ fn compare_footnote_definition<'b, 's>(
|
||||
let mut message = None;
|
||||
// TODO: Compare :pre-blank
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
// Compare label
|
||||
let label = get_property_quoted_string(emacs, ":label")?
|
||||
.ok_or("Footnote definitions should have a name.")?;
|
||||
@ -1245,10 +1326,10 @@ fn compare_drawer<'b, 's>(
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// Compare drawer-name
|
||||
let name =
|
||||
get_property_quoted_string(emacs, ":drawer-name")?.ok_or("Drawers should have a name.")?;
|
||||
if name != rust.name {
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
@ -1256,6 +1337,17 @@ fn compare_drawer<'b, 's>(
|
||||
));
|
||||
}
|
||||
|
||||
// Compare drawer-name
|
||||
let drawer_name =
|
||||
get_property_quoted_string(emacs, ":drawer-name")?.ok_or("Drawers should have a name.")?;
|
||||
if drawer_name != rust.drawer_name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Drawer name mismatch (emacs != rust) {:?} != {:?}",
|
||||
drawer_name, rust.drawer_name
|
||||
));
|
||||
}
|
||||
|
||||
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
|
||||
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
|
||||
}
|
||||
@ -1308,11 +1400,11 @@ fn compare_node_property<'b, 's>(
|
||||
// Compare key
|
||||
let key =
|
||||
get_property_quoted_string(emacs, ":key")?.ok_or("Node properties should have a key.")?;
|
||||
if key != rust.name {
|
||||
if key != rust.property_name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Key mismatch (emacs != rust) {:?} != {:?}",
|
||||
key, rust.name
|
||||
key, rust.property_name
|
||||
));
|
||||
}
|
||||
|
||||
@ -1351,6 +1443,17 @@ fn compare_table<'b, 's>(
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
// Compare formulas
|
||||
//
|
||||
// :tblfm is either nil or a list () filled with quoted strings containing the value for any tblfm keywords at the end of the table.
|
||||
@ -1492,8 +1595,19 @@ fn compare_verse_block<'b, 's>(
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let child_status = Vec::new();
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
for (_emacs_child, _rust_child) in children.iter().skip(2).zip(rust.children.iter()) {}
|
||||
|
||||
@ -1513,10 +1627,29 @@ fn compare_comment_block<'b, 's>(
|
||||
emacs: &'b Token<'s>,
|
||||
rust: &'b CommentBlock<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :value
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
// Compare value
|
||||
let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new());
|
||||
if contents != rust.contents {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Value mismatch (emacs != rust) {:?} != {:?}",
|
||||
contents, rust.contents
|
||||
));
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
@ -1533,11 +1666,165 @@ fn compare_example_block<'b, 's>(
|
||||
_source: &'s str,
|
||||
emacs: &'b Token<'s>,
|
||||
rust: &'b ExampleBlock<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error + 's>> {
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :value :switches :number-lines :preserve-indent :retain-labels :use-labels :label-fmt
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
// Compare value
|
||||
let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new());
|
||||
if contents != rust.contents {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Value mismatch (emacs != rust) {:?} != {:?}",
|
||||
contents, rust.contents
|
||||
));
|
||||
}
|
||||
|
||||
// Compare switches
|
||||
let switches = get_property_quoted_string(emacs, ":switches")?;
|
||||
match (switches.as_ref().map(String::as_str), rust.switches) {
|
||||
(None, None) => {}
|
||||
(Some(""), None) => {}
|
||||
(None, Some("")) => {
|
||||
unreachable!("The organic parser would return a None instead of an empty string.");
|
||||
}
|
||||
(Some(e), Some(r)) if e == r => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Switches mismatch (emacs != rust) {:?} != {:?}",
|
||||
switches, rust.switches
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Compare number-lines
|
||||
let number_lines = get_property(emacs, ":number-lines")?;
|
||||
match (number_lines, &rust.number_lines) {
|
||||
(None, None) => {}
|
||||
(Some(number_lines), Some(rust_number_lines)) => {
|
||||
let token_list = number_lines.as_list()?;
|
||||
let number_type = token_list
|
||||
.get(0)
|
||||
.map(Token::as_atom)
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.ok_or(":number-lines should have a type.")?;
|
||||
let number_value = token_list
|
||||
.get(2)
|
||||
.map(Token::as_atom)
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.map(|val| val.parse::<LineNumber>())
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.ok_or(":number-lines should have a value.")?;
|
||||
match (number_type, number_value, rust_number_lines) {
|
||||
("new", emacs_val, SwitchNumberLines::New(rust_val)) if emacs_val == *rust_val => {}
|
||||
("continued", emacs_val, SwitchNumberLines::Continued(rust_val))
|
||||
if emacs_val == *rust_val => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Number lines mismatch (emacs != rust) {:?} != {:?}",
|
||||
number_lines, rust.number_lines
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Number lines mismatch (emacs != rust) {:?} != {:?}",
|
||||
number_lines, rust.number_lines
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Compare preserve-indent
|
||||
let preserve_indent: Option<CharOffsetInLine> =
|
||||
get_property_numeric(emacs, ":preserve-indent")?;
|
||||
if preserve_indent != rust.preserve_indent {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Prserve indent mismatch (emacs != rust) {:?} != {:?}",
|
||||
preserve_indent, rust.preserve_indent
|
||||
));
|
||||
}
|
||||
|
||||
// Compare retain-labels
|
||||
// retain-labels is t by default, nil if -r is set, or a number if -k and -r is set.
|
||||
let retain_labels = get_property_unquoted_atom(emacs, ":retain-labels")?;
|
||||
if let Some(retain_labels) = retain_labels {
|
||||
if retain_labels == "t" {
|
||||
match rust.retain_labels {
|
||||
RetainLabels::Yes => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
||||
retain_labels, rust.retain_labels
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let retain_labels: CharOffsetInLine = get_property_numeric(emacs, ":retain-labels")?.expect("Cannot be None or else the earlier get_property_unquoted_atom would have been None.");
|
||||
match (retain_labels, &rust.retain_labels) {
|
||||
(e, RetainLabels::Keep(r)) if e == *r => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
||||
retain_labels, rust.retain_labels
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match rust.retain_labels {
|
||||
RetainLabels::No => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
||||
retain_labels, rust.retain_labels
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compare use-labels
|
||||
let use_labels = get_property_boolean(emacs, ":use-labels")?;
|
||||
if use_labels != rust.use_labels {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Use labels mismatch (emacs != rust) {:?} != {:?}",
|
||||
use_labels, rust.use_labels
|
||||
));
|
||||
}
|
||||
|
||||
// Compare label-fmt
|
||||
let label_format = get_property_quoted_string(emacs, ":label-fmt")?;
|
||||
match (label_format.as_ref(), rust.label_format) {
|
||||
(None, None) => {}
|
||||
(Some(emacs_label_format), Some(rust_label_format))
|
||||
if emacs_label_format == rust_label_format => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Label format mismatch (emacs != rust) {:?} != {:?}",
|
||||
label_format, rust.label_format
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
@ -1555,10 +1842,41 @@ fn compare_export_block<'b, 's>(
|
||||
emacs: &'b Token<'s>,
|
||||
rust: &'b ExportBlock<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :type :value
|
||||
// TODO: Compare :caption
|
||||
|
||||
// Compare type
|
||||
let export_type = get_property_quoted_string(emacs, ":type")?;
|
||||
let rust_export_type = rust.get_export_type();
|
||||
if export_type != rust_export_type {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Export type mismatch (emacs != rust) {:?} != {:?}",
|
||||
export_type, rust.export_type
|
||||
));
|
||||
}
|
||||
|
||||
// Compare value
|
||||
let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new());
|
||||
if contents != rust.contents {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Value mismatch (emacs != rust) {:?} != {:?}",
|
||||
contents, rust.contents
|
||||
));
|
||||
}
|
||||
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
@ -1575,11 +1893,193 @@ fn compare_src_block<'b, 's>(
|
||||
_source: &'s str,
|
||||
emacs: &'b Token<'s>,
|
||||
rust: &'b SrcBlock<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error + 's>> {
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :language :switches :parameters :number-lines :preserve-indent :retain-labels :use-labels :label-fmt :value
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
// Compare language
|
||||
let language = get_property_quoted_string(emacs, ":language")?;
|
||||
if language.as_ref().map(String::as_str) != rust.language {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Language mismatch (emacs != rust) {:?} != {:?}",
|
||||
language, rust.language
|
||||
));
|
||||
}
|
||||
|
||||
// Compare value
|
||||
let contents = get_property_quoted_string(emacs, ":value")?.unwrap_or(String::new());
|
||||
if contents != rust.contents {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Value mismatch (emacs != rust) {:?} != {:?}",
|
||||
contents, rust.contents
|
||||
));
|
||||
}
|
||||
|
||||
// Compare switches
|
||||
let switches = get_property_quoted_string(emacs, ":switches")?;
|
||||
match (switches.as_ref().map(String::as_str), rust.switches) {
|
||||
(None, None) => {}
|
||||
(Some(""), None) => {}
|
||||
(None, Some("")) => {
|
||||
unreachable!("The organic parser would return a None instead of an empty string.");
|
||||
}
|
||||
(Some(e), Some(r)) if e == r => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Switches mismatch (emacs != rust) {:?} != {:?}",
|
||||
switches, rust.switches
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Compare parameters
|
||||
let parameters = get_property_quoted_string(emacs, ":parameters")?;
|
||||
match (parameters.as_ref().map(String::as_str), rust.parameters) {
|
||||
(None, None) => {}
|
||||
(Some(""), None) => {}
|
||||
(None, Some("")) => {
|
||||
unreachable!("The organic parser would return a None instead of an empty string.");
|
||||
}
|
||||
(Some(e), Some(r)) if e == r => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Parameters mismatch (emacs != rust) {:?} != {:?}",
|
||||
parameters, rust.parameters
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Compare number-lines
|
||||
let number_lines = get_property(emacs, ":number-lines")?;
|
||||
match (number_lines, &rust.number_lines) {
|
||||
(None, None) => {}
|
||||
(Some(number_lines), Some(rust_number_lines)) => {
|
||||
let token_list = number_lines.as_list()?;
|
||||
let number_type = token_list
|
||||
.get(0)
|
||||
.map(Token::as_atom)
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.ok_or(":number-lines should have a type.")?;
|
||||
let number_value = token_list
|
||||
.get(2)
|
||||
.map(Token::as_atom)
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.map(|val| val.parse::<LineNumber>())
|
||||
.map_or(Ok(None), |r| r.map(Some))?
|
||||
.ok_or(":number-lines should have a value.")?;
|
||||
match (number_type, number_value, rust_number_lines) {
|
||||
("new", emacs_val, SwitchNumberLines::New(rust_val)) if emacs_val == *rust_val => {}
|
||||
("continued", emacs_val, SwitchNumberLines::Continued(rust_val))
|
||||
if emacs_val == *rust_val => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Number lines mismatch (emacs != rust) {:?} != {:?}",
|
||||
number_lines, rust.number_lines
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Number lines mismatch (emacs != rust) {:?} != {:?}",
|
||||
number_lines, rust.number_lines
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Compare preserve-indent
|
||||
let preserve_indent: Option<CharOffsetInLine> =
|
||||
get_property_numeric(emacs, ":preserve-indent")?;
|
||||
if preserve_indent != rust.preserve_indent {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Prserve indent mismatch (emacs != rust) {:?} != {:?}",
|
||||
preserve_indent, rust.preserve_indent
|
||||
));
|
||||
}
|
||||
|
||||
// Compare retain-labels
|
||||
// retain-labels is t by default, nil if -r is set, or a number if -k and -r is set.
|
||||
let retain_labels = get_property_unquoted_atom(emacs, ":retain-labels")?;
|
||||
if let Some(retain_labels) = retain_labels {
|
||||
if retain_labels == "t" {
|
||||
match rust.retain_labels {
|
||||
RetainLabels::Yes => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
||||
retain_labels, rust.retain_labels
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let retain_labels: CharOffsetInLine = get_property_numeric(emacs, ":retain-labels")?.expect("Cannot be None or else the earlier get_property_unquoted_atom would have been None.");
|
||||
match (retain_labels, &rust.retain_labels) {
|
||||
(e, RetainLabels::Keep(r)) if e == *r => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
||||
retain_labels, rust.retain_labels
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match rust.retain_labels {
|
||||
RetainLabels::No => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Retain labels mismatch (emacs != rust) {:?} != {:?}",
|
||||
retain_labels, rust.retain_labels
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compare use-labels
|
||||
let use_labels = get_property_boolean(emacs, ":use-labels")?;
|
||||
if use_labels != rust.use_labels {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Use labels mismatch (emacs != rust) {:?} != {:?}",
|
||||
use_labels, rust.use_labels
|
||||
));
|
||||
}
|
||||
|
||||
// Compare label-fmt
|
||||
let label_format = get_property_quoted_string(emacs, ":label-fmt")?;
|
||||
match (label_format.as_ref(), rust.label_format) {
|
||||
(None, None) => {}
|
||||
(Some(emacs_label_format), Some(rust_label_format))
|
||||
if emacs_label_format == rust_label_format => {}
|
||||
_ => {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Label format mismatch (emacs != rust) {:?} != {:?}",
|
||||
label_format, rust.label_format
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
@ -1618,11 +2118,22 @@ fn compare_diary_sexp<'b, 's>(
|
||||
emacs: &'b Token<'s>,
|
||||
rust: &'b DiarySexp<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :value
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: rust.get_elisp_name(),
|
||||
@ -1711,11 +2222,22 @@ fn compare_fixed_width_area<'b, 's>(
|
||||
rust: &'b FixedWidthArea<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let child_status = Vec::new();
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :value
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: rust.get_elisp_name(),
|
||||
@ -1733,8 +2255,19 @@ fn compare_horizontal_rule<'b, 's>(
|
||||
rust: &'b HorizontalRule<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let child_status = Vec::new();
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
@ -1756,6 +2289,17 @@ fn compare_keyword<'b, 's>(
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
let key = unquote(
|
||||
get_property(emacs, ":key")?
|
||||
.ok_or("Emacs keywords should have a :key")?
|
||||
@ -1802,6 +2346,18 @@ fn compare_babel_call<'b, 's>(
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :call :inside-header :arguments :end-header
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
let value = unquote(
|
||||
get_property(emacs, ":value")?
|
||||
.ok_or("Emacs keywords should have a :value")?
|
||||
@ -1832,11 +2388,22 @@ fn compare_latex_environment<'b, 's>(
|
||||
rust: &'b LatexEnvironment<'s>,
|
||||
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
|
||||
let child_status = Vec::new();
|
||||
let this_status = DiffStatus::Good;
|
||||
let message = None;
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut message = None;
|
||||
|
||||
// TODO: Compare :value
|
||||
|
||||
// TODO: Compare :caption
|
||||
// Compare name
|
||||
let name = get_property_quoted_string(emacs, ":name")?;
|
||||
if name.as_ref().map(String::as_str) != rust.name {
|
||||
this_status = DiffStatus::Bad;
|
||||
message = Some(format!(
|
||||
"Name mismatch (emacs != rust) {:?} != {:?}",
|
||||
name, rust.name
|
||||
));
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: rust.get_elisp_name(),
|
||||
|
@ -12,6 +12,7 @@ use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::OrgSource;
|
||||
use crate::types::Keyword;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum ContextElement<'r, 's> {
|
||||
@ -27,11 +28,22 @@ pub(crate) enum ContextElement<'r, 's> {
|
||||
/// Indicates if elements should consume the whitespace after them.
|
||||
ConsumeTrailingWhitespace(bool),
|
||||
|
||||
/// Indicate that we are parsing a paragraph that already has affiliated keywords.
|
||||
///
|
||||
/// The value stored is the start of the element after the affiliated keywords. In this way, we can ensure that we do not exit an element immediately after the affiliated keyword had been consumed.
|
||||
HasAffiliatedKeyword(HasAffiliatedKeywordInner<'r, 's>),
|
||||
|
||||
/// This is just here to use the 's lifetime until I'm sure we can eliminate it from ContextElement.
|
||||
#[allow(dead_code)]
|
||||
Placeholder(PhantomData<&'s str>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct HasAffiliatedKeywordInner<'r, 's> {
|
||||
pub(crate) start_after_affiliated_keywords: OrgSource<'s>,
|
||||
pub(crate) keywords: &'r Vec<Keyword<'s>>,
|
||||
}
|
||||
|
||||
pub(crate) struct ExitMatcherNode<'r> {
|
||||
// TODO: Should this be "&'r DynContextMatcher<'c>" ?
|
||||
pub(crate) exit_matcher: &'r DynContextMatcher<'r>,
|
||||
|
@ -32,6 +32,11 @@ pub struct GlobalSettings<'g, 's> {
|
||||
///
|
||||
/// Corresponds to org-footnote-section elisp variable.
|
||||
pub footnote_section: &'g str,
|
||||
|
||||
/// The label format for references inside src/example blocks.
|
||||
///
|
||||
/// Corresponds to org-coderef-label-format elisp variable.
|
||||
pub coderef_label_format: &'g str,
|
||||
}
|
||||
|
||||
pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8;
|
||||
@ -49,6 +54,7 @@ impl<'g, 's> GlobalSettings<'g, 's> {
|
||||
tab_width: DEFAULT_TAB_WIDTH,
|
||||
odd_levels_only: HeadlineLevelFilter::default(),
|
||||
footnote_section: "Footnotes",
|
||||
coderef_label_format: "(ref:%s)",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ type DynMatcher<'c> = dyn Matcher + 'c;
|
||||
pub(crate) use context::Context;
|
||||
pub(crate) use context::ContextElement;
|
||||
pub(crate) use context::ExitMatcherNode;
|
||||
pub(crate) use context::HasAffiliatedKeywordInner;
|
||||
pub(crate) use exiting::ExitClass;
|
||||
pub use file_access_interface::FileAccessInterface;
|
||||
pub use file_access_interface::LocalFileAccessInterface;
|
||||
|
@ -3,9 +3,12 @@ use nom::bytes::complete::is_not;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::combinator::eof;
|
||||
use nom::multi::many0;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::get_consumed;
|
||||
@ -17,6 +20,7 @@ pub(crate) fn diary_sexp<'b, 'g, 'r, 's>(
|
||||
_context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, DiarySexp<'s>> {
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
start_of_line(input)?;
|
||||
let (remaining, _clock) = tag("%%(")(input)?;
|
||||
let (remaining, _contents) = is_not("\r\n")(remaining)?;
|
||||
@ -27,12 +31,14 @@ pub(crate) fn diary_sexp<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
DiarySexp {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn detect_diary_sexp<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||
tuple((start_of_line, tag("%%(")))(input)?;
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
@ -7,10 +7,13 @@ use nom::character::complete::space0;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::not;
|
||||
use nom::combinator::recognize;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::ExitClass;
|
||||
@ -41,6 +44,7 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>(
|
||||
"Cannot nest objects of the same element".into(),
|
||||
))));
|
||||
}
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
start_of_line(input)?;
|
||||
let (remaining, _leading_whitespace) = space0(input)?;
|
||||
let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple((
|
||||
@ -90,7 +94,8 @@ pub(crate) fn drawer<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Drawer {
|
||||
source: source.into(),
|
||||
name: drawer_name.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
drawer_name: drawer_name.into(),
|
||||
children,
|
||||
},
|
||||
))
|
||||
|
@ -17,7 +17,9 @@ use nom::multi::many_till;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::ExitClass;
|
||||
@ -47,6 +49,8 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>(
|
||||
"Cannot nest objects of the same element".into(),
|
||||
))));
|
||||
}
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
|
||||
start_of_line(input)?;
|
||||
let (remaining, _leading_whitespace) = space0(input)?;
|
||||
let (remaining, (_, name, parameters, _, _)) = tuple((
|
||||
@ -97,7 +101,8 @@ pub(crate) fn dynamic_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
DynamicBlock {
|
||||
source: source.into(),
|
||||
name: name.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
block_name: name.into(),
|
||||
parameters: parameters.map(|val| val.into()),
|
||||
children,
|
||||
},
|
||||
|
@ -1,6 +1,10 @@
|
||||
use nom::branch::alt;
|
||||
use nom::combinator::map;
|
||||
use nom::multi::many0;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::peek;
|
||||
use nom::sequence::tuple;
|
||||
#[cfg(feature = "tracing")]
|
||||
use tracing::span;
|
||||
|
||||
use super::clock::clock;
|
||||
use super::comment::comment;
|
||||
@ -72,52 +76,95 @@ fn _element<'b, 'g, 'r, 's>(
|
||||
let fixed_width_area_matcher = parser_with_context!(fixed_width_area)(context);
|
||||
let horizontal_rule_matcher = parser_with_context!(horizontal_rule)(context);
|
||||
let keyword_matcher = parser_with_context!(keyword)(context);
|
||||
let affiliated_keyword_matcher = parser_with_context!(affiliated_keyword)(context);
|
||||
let babel_keyword_matcher = parser_with_context!(babel_call_keyword)(context);
|
||||
let paragraph_matcher = parser_with_context!(paragraph)(context);
|
||||
let latex_environment_matcher = parser_with_context!(latex_environment)(context);
|
||||
|
||||
// TODO: Affiliated keywords cannot be on comments, clocks, headings, inlinetasks, items, node properties, planning, property drawers, sections, and table rows
|
||||
let (remaining, mut affiliated_keywords) = many0(affiliated_keyword_matcher)(input)?;
|
||||
let (remaining, mut element) = match alt((
|
||||
map(plain_list_matcher, Element::PlainList),
|
||||
greater_block_matcher,
|
||||
map(dynamic_block_matcher, Element::DynamicBlock),
|
||||
map(footnote_definition_matcher, Element::FootnoteDefinition),
|
||||
map(comment_matcher, Element::Comment),
|
||||
map(drawer_matcher, Element::Drawer),
|
||||
map(table_matcher, Element::Table),
|
||||
map(verse_block_matcher, Element::VerseBlock),
|
||||
map(comment_block_matcher, Element::CommentBlock),
|
||||
map(example_block_matcher, Element::ExampleBlock),
|
||||
map(export_block_matcher, Element::ExportBlock),
|
||||
map(src_block_matcher, Element::SrcBlock),
|
||||
map(clock_matcher, Element::Clock),
|
||||
map(diary_sexp_matcher, Element::DiarySexp),
|
||||
map(fixed_width_area_matcher, Element::FixedWidthArea),
|
||||
map(horizontal_rule_matcher, Element::HorizontalRule),
|
||||
map(latex_environment_matcher, Element::LatexEnvironment),
|
||||
map(babel_keyword_matcher, Element::BabelCall),
|
||||
map(keyword_matcher, Element::Keyword),
|
||||
))(remaining)
|
||||
{
|
||||
the_ok @ Ok(_) => the_ok,
|
||||
Err(_) => {
|
||||
if can_be_paragraph {
|
||||
match map(paragraph_matcher, Element::Paragraph)(remaining) {
|
||||
the_ok @ Ok(_) => the_ok,
|
||||
Err(_) => {
|
||||
// TODO: Because this function expects a single element, if there are multiple affiliated keywords before an element that cannot have affiliated keywords, we end up re-parsing the affiliated keywords many times.
|
||||
affiliated_keywords.clear();
|
||||
map(affiliated_keyword_matcher, Element::Keyword)(input)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
affiliated_keywords.clear();
|
||||
map(affiliated_keyword_matcher, Element::Keyword)(input)
|
||||
}
|
||||
let (mut remaining, mut maybe_element) = {
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = span!(tracing::Level::DEBUG, "Main element block");
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
|
||||
opt(alt((
|
||||
map(plain_list_matcher, Element::PlainList),
|
||||
greater_block_matcher,
|
||||
map(dynamic_block_matcher, Element::DynamicBlock),
|
||||
map(footnote_definition_matcher, Element::FootnoteDefinition),
|
||||
map(comment_matcher, Element::Comment),
|
||||
map(drawer_matcher, Element::Drawer),
|
||||
map(table_matcher, Element::Table),
|
||||
map(verse_block_matcher, Element::VerseBlock),
|
||||
map(comment_block_matcher, Element::CommentBlock),
|
||||
map(example_block_matcher, Element::ExampleBlock),
|
||||
map(export_block_matcher, Element::ExportBlock),
|
||||
map(src_block_matcher, Element::SrcBlock),
|
||||
map(clock_matcher, Element::Clock),
|
||||
map(diary_sexp_matcher, Element::DiarySexp),
|
||||
map(fixed_width_area_matcher, Element::FixedWidthArea),
|
||||
map(horizontal_rule_matcher, Element::HorizontalRule),
|
||||
map(latex_environment_matcher, Element::LatexEnvironment),
|
||||
map(babel_keyword_matcher, Element::BabelCall),
|
||||
map(keyword_matcher, Element::Keyword),
|
||||
)))(input)?
|
||||
};
|
||||
|
||||
if maybe_element.is_none() && can_be_paragraph {
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = span!(tracing::Level::DEBUG, "Paragraph with affiliated keyword.");
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
|
||||
let (remain, paragraph_with_affiliated_keyword) = opt(map(
|
||||
tuple((
|
||||
peek(affiliated_keyword),
|
||||
map(paragraph_matcher, Element::Paragraph),
|
||||
)),
|
||||
|(_, paragraph)| paragraph,
|
||||
))(remaining)?;
|
||||
if paragraph_with_affiliated_keyword.is_some() {
|
||||
remaining = remain;
|
||||
maybe_element = paragraph_with_affiliated_keyword;
|
||||
}
|
||||
}?;
|
||||
}
|
||||
|
||||
if maybe_element.is_none() {
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = span!(
|
||||
tracing::Level::DEBUG,
|
||||
"Affiliated keyword as regular keyword."
|
||||
);
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
|
||||
let (remain, kw) = opt(map(affiliated_keyword, Element::Keyword))(remaining)?;
|
||||
if kw.is_some() {
|
||||
maybe_element = kw;
|
||||
remaining = remain;
|
||||
}
|
||||
}
|
||||
|
||||
if maybe_element.is_none() && can_be_paragraph {
|
||||
#[cfg(feature = "tracing")]
|
||||
let span = span!(
|
||||
tracing::Level::DEBUG,
|
||||
"Paragraph without affiliated keyword."
|
||||
);
|
||||
#[cfg(feature = "tracing")]
|
||||
let _enter = span.enter();
|
||||
|
||||
let (remain, paragraph_without_affiliated_keyword) =
|
||||
map(paragraph_matcher, Element::Paragraph)(remaining)?;
|
||||
remaining = remain;
|
||||
maybe_element = Some(paragraph_without_affiliated_keyword);
|
||||
}
|
||||
|
||||
if maybe_element.is_none() {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No element.",
|
||||
))));
|
||||
}
|
||||
let mut element = maybe_element.expect("The above if-statement ensures this is Some().");
|
||||
|
||||
let (remaining, _trailing_ws) =
|
||||
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
|
||||
|
@ -10,7 +10,9 @@ use nom::multi::many0;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::only_space1;
|
||||
use super::util::org_line_ending;
|
||||
use crate::context::parser_with_context;
|
||||
@ -26,6 +28,7 @@ pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, FixedWidthArea<'s>> {
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let fixed_width_area_line_matcher = parser_with_context!(fixed_width_area_line)(context);
|
||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(context);
|
||||
let (remaining, _first_line) = fixed_width_area_line_matcher(input)?;
|
||||
@ -37,6 +40,7 @@ pub(crate) fn fixed_width_area<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
FixedWidthArea {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
},
|
||||
))
|
||||
}
|
||||
@ -59,6 +63,7 @@ fn fixed_width_area_line<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn detect_fixed_width_area<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||
tuple((
|
||||
start_of_line,
|
||||
space0,
|
||||
|
@ -11,7 +11,9 @@ use nom::multi::many1;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::include_input;
|
||||
use super::util::WORD_CONSTITUENT_CHARACTERS;
|
||||
use crate::context::parser_with_context;
|
||||
@ -41,6 +43,7 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>(
|
||||
"Cannot nest objects of the same element".into(),
|
||||
))));
|
||||
}
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
start_of_line(input)?;
|
||||
// Cannot be indented.
|
||||
let (remaining, (_, lbl, _, _, _)) = tuple((
|
||||
@ -85,6 +88,7 @@ pub(crate) fn footnote_definition<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
FootnoteDefinition {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
label: lbl.into(),
|
||||
children: children.into_iter().map(|(_, item)| item).collect(),
|
||||
},
|
||||
@ -119,6 +123,7 @@ fn footnote_definition_end<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn detect_footnote_definition<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||
tuple((start_of_line, tag_no_case("[fn:"), label, tag("]")))(input)?;
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
@ -17,7 +17,9 @@ use nom::multi::many_till;
|
||||
use nom::sequence::preceded;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::in_section;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
@ -35,6 +37,7 @@ use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::types::CenterBlock;
|
||||
use crate::types::Element;
|
||||
use crate::types::Keyword;
|
||||
use crate::types::Paragraph;
|
||||
use crate::types::QuoteBlock;
|
||||
use crate::types::SetSource;
|
||||
@ -45,6 +48,7 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
start_of_line(input)?;
|
||||
let (remaining, _leading_whitespace) = space0(input)?;
|
||||
let (remaining, (_begin, name)) = tuple((
|
||||
@ -58,9 +62,9 @@ pub(crate) fn greater_block<'b, 'g, 'r, 's>(
|
||||
))(remaining)?;
|
||||
let name = Into::<&str>::into(name);
|
||||
let (remaining, element) = match name.to_lowercase().as_str() {
|
||||
"center" => center_block(context, remaining, input)?,
|
||||
"quote" => quote_block(context, remaining, input)?,
|
||||
_ => special_block(name)(context, remaining, input)?,
|
||||
"center" => center_block(context, remaining, input, &affiliated_keywords)?,
|
||||
"quote" => quote_block(context, remaining, input, &affiliated_keywords)?,
|
||||
_ => special_block(name)(context, remaining, input, &affiliated_keywords)?,
|
||||
};
|
||||
Ok((remaining, element))
|
||||
}
|
||||
@ -70,12 +74,17 @@ fn center_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
original_input: OrgSource<'s>,
|
||||
affiliated_keywords: &Vec<Keyword<'s>>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
let (remaining, (source, children)) =
|
||||
greater_block_body(context, input, original_input, "center", "center block")?;
|
||||
Ok((
|
||||
remaining,
|
||||
Element::CenterBlock(CenterBlock { source, children }),
|
||||
Element::CenterBlock(CenterBlock {
|
||||
source,
|
||||
name: get_name(&affiliated_keywords),
|
||||
children,
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
@ -84,12 +93,17 @@ fn quote_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
original_input: OrgSource<'s>,
|
||||
affiliated_keywords: &Vec<Keyword<'s>>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
let (remaining, (source, children)) =
|
||||
greater_block_body(context, input, original_input, "quote", "quote block")?;
|
||||
Ok((
|
||||
remaining,
|
||||
Element::QuoteBlock(QuoteBlock { source, children }),
|
||||
Element::QuoteBlock(QuoteBlock {
|
||||
source,
|
||||
name: get_name(&affiliated_keywords),
|
||||
children,
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
@ -99,11 +113,19 @@ fn special_block<'s>(
|
||||
RefContext<'b, 'g, 'r, 's>,
|
||||
OrgSource<'s>,
|
||||
OrgSource<'s>,
|
||||
&Vec<Keyword<'s>>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>>
|
||||
+ 's {
|
||||
let context_name = format!("special block {}", name);
|
||||
move |context, input, original_input| {
|
||||
_special_block(context, input, original_input, name, context_name.as_str())
|
||||
move |context, input, original_input, affiliated_keywords| {
|
||||
_special_block(
|
||||
context,
|
||||
input,
|
||||
original_input,
|
||||
name,
|
||||
context_name.as_str(),
|
||||
affiliated_keywords,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,6 +136,7 @@ fn _special_block<'c, 'b, 'g, 'r, 's>(
|
||||
original_input: OrgSource<'s>,
|
||||
name: &'s str,
|
||||
context_name: &'c str,
|
||||
affiliated_keywords: &Vec<Keyword<'s>>,
|
||||
) -> Res<OrgSource<'s>, Element<'s>> {
|
||||
let (remaining, parameters) = opt(tuple((space1, parameters)))(input)?;
|
||||
let (remaining, (source, children)) =
|
||||
@ -122,8 +145,9 @@ fn _special_block<'c, 'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Element::SpecialBlock(SpecialBlock {
|
||||
source,
|
||||
name: get_name(&affiliated_keywords),
|
||||
children,
|
||||
name,
|
||||
block_type: name,
|
||||
parameters: parameters.map(|(_, parameters)| Into::<&str>::into(parameters)),
|
||||
}),
|
||||
))
|
||||
|
@ -5,10 +5,13 @@ use nom::character::complete::space0;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many1_count;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::Res;
|
||||
use crate::parser::util::start_of_line;
|
||||
@ -19,6 +22,7 @@ pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>(
|
||||
_context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, HorizontalRule<'s>> {
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
start_of_line(input)?;
|
||||
let (remaining, rule) = recognize(tuple((
|
||||
space0,
|
||||
@ -30,6 +34,7 @@ pub(crate) fn horizontal_rule<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
HorizontalRule {
|
||||
source: rule.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -13,11 +13,13 @@ use nom::combinator::not;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::org_source::BracketDepth;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use crate::context::Matcher;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
@ -61,6 +63,7 @@ fn _filtered_keyword<'s, F: Matcher>(
|
||||
remaining,
|
||||
Keyword {
|
||||
source: consumed_input.into(),
|
||||
name: None, // To be populated by the caller if this keyword is in a context to support affiliated keywords.
|
||||
key: parsed_key.into(),
|
||||
value: "".into(),
|
||||
},
|
||||
@ -78,6 +81,7 @@ fn _filtered_keyword<'s, F: Matcher>(
|
||||
remaining,
|
||||
Keyword {
|
||||
source: consumed_input.into(),
|
||||
name: None, // To be populated by the caller if this keyword is in a context to support affiliated keywords.
|
||||
key: parsed_key.into(),
|
||||
value: parsed_value.into(),
|
||||
},
|
||||
@ -89,14 +93,14 @@ pub(crate) fn keyword<'b, 'g, 'r, 's>(
|
||||
_context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||
filtered_keyword(regular_keyword_key)(input)
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let (remaining, mut kw) = filtered_keyword(regular_keyword_key)(input)?;
|
||||
kw.name = get_name(&affiliated_keywords);
|
||||
Ok((remaining, kw))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn affiliated_keyword<'b, 'g, 'r, 's>(
|
||||
_context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||
pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
|
||||
filtered_keyword(affiliated_key)(input)
|
||||
}
|
||||
|
||||
@ -105,11 +109,13 @@ pub(crate) fn babel_call_keyword<'b, 'g, 'r, 's>(
|
||||
_context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, BabelCall<'s>> {
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let (remaining, kw) = filtered_keyword(babel_call_key)(input)?;
|
||||
Ok((
|
||||
remaining,
|
||||
BabelCall {
|
||||
source: kw.source,
|
||||
name: get_name(&affiliated_keywords),
|
||||
key: kw.key,
|
||||
value: kw.value,
|
||||
},
|
||||
|
@ -8,11 +8,14 @@ use nom::character::complete::space0;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_consumed;
|
||||
use super::util::get_name;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::ContextMatcher;
|
||||
@ -29,6 +32,7 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, LatexEnvironment<'s>> {
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
start_of_line(input)?;
|
||||
let (remaining, _leading_whitespace) = space0(input)?;
|
||||
let (remaining, (_opening, name, _open_close_brace, _ws, _line_ending)) = tuple((
|
||||
@ -54,6 +58,7 @@ pub(crate) fn latex_environment<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
LatexEnvironment {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -1,24 +1,33 @@
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::is_not;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::character::complete::anychar;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::space0;
|
||||
use nom::character::complete::space1;
|
||||
use nom::combinator::consumed;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::map;
|
||||
use nom::combinator::opt;
|
||||
use nom::combinator::peek;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::ContextMatcher;
|
||||
use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::object_parser::standard_set_object;
|
||||
use crate::parser::util::blank_line;
|
||||
@ -26,12 +35,16 @@ use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::parser::util::text_until_exit;
|
||||
use crate::types::CharOffsetInLine;
|
||||
use crate::types::CommentBlock;
|
||||
use crate::types::ExampleBlock;
|
||||
use crate::types::ExportBlock;
|
||||
use crate::types::LineNumber;
|
||||
use crate::types::Object;
|
||||
use crate::types::PlainText;
|
||||
use crate::types::RetainLabels;
|
||||
use crate::types::SrcBlock;
|
||||
use crate::types::SwitchNumberLines;
|
||||
use crate::types::VerseBlock;
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
@ -39,7 +52,8 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, VerseBlock<'s>> {
|
||||
let (remaining, name) = lesser_block_begin("verse")(context, input)?;
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let (remaining, _) = lesser_block_begin("verse")(context, input)?;
|
||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||
let lesser_block_end_specialized = lesser_block_end("verse");
|
||||
@ -82,7 +96,7 @@ pub(crate) fn verse_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
VerseBlock {
|
||||
source: source.into(),
|
||||
name: name.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
||||
children,
|
||||
},
|
||||
@ -94,8 +108,9 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, CommentBlock<'s>> {
|
||||
let (remaining, name) = lesser_block_begin("comment")(context, input)?;
|
||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let (remaining, _) = lesser_block_begin("comment")(context, input)?;
|
||||
let (remaining, _parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||
let lesser_block_end_specialized = lesser_block_end("comment");
|
||||
let contexts = [
|
||||
@ -109,10 +124,6 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>(
|
||||
let parser_context = context.with_additional_node(&contexts[0]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||
let parameters = match parameters {
|
||||
Some((_ws, parameters)) => Some(parameters),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||
@ -122,8 +133,7 @@ pub(crate) fn comment_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
CommentBlock {
|
||||
source: source.into(),
|
||||
name: name.into(),
|
||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
||||
name: get_name(&affiliated_keywords),
|
||||
contents: contents.into(),
|
||||
},
|
||||
))
|
||||
@ -134,8 +144,9 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, ExampleBlock<'s>> {
|
||||
let (remaining, _name) = lesser_block_begin("example")(context, input)?;
|
||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let (remaining, _) = lesser_block_begin("example")(context, input)?;
|
||||
let (remaining, parameters) = opt(tuple((space1, example_switches)))(remaining)?;
|
||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||
let lesser_block_end_specialized = lesser_block_end("example");
|
||||
let contexts = [
|
||||
@ -149,22 +160,42 @@ pub(crate) fn example_block<'b, 'g, 'r, 's>(
|
||||
let parser_context = context.with_additional_node(&contexts[0]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||
let parameters = match parameters {
|
||||
Some((_ws, parameters)) => Some(parameters),
|
||||
None => None,
|
||||
};
|
||||
let parameters = parameters.map(|(_, parameters)| parameters);
|
||||
|
||||
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
||||
let (remaining, contents) = content(&parser_context, remaining)?;
|
||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||
|
||||
let source = get_consumed(input, remaining);
|
||||
let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = {
|
||||
if let Some(parameters) = parameters {
|
||||
(
|
||||
if parameters.source.len() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(parameters.source)
|
||||
},
|
||||
parameters.number_lines,
|
||||
parameters.preserve_indent,
|
||||
parameters.retain_labels,
|
||||
parameters.use_labels,
|
||||
parameters.label_format,
|
||||
)
|
||||
} else {
|
||||
(None, None, None, RetainLabels::Yes, true, None)
|
||||
}
|
||||
};
|
||||
Ok((
|
||||
remaining,
|
||||
ExampleBlock {
|
||||
source: source.into(),
|
||||
name: source.into(),
|
||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
||||
contents: contents.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
switches,
|
||||
number_lines,
|
||||
preserve_indent,
|
||||
retain_labels,
|
||||
use_labels,
|
||||
label_format,
|
||||
contents,
|
||||
},
|
||||
))
|
||||
}
|
||||
@ -174,9 +205,15 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, ExportBlock<'s>> {
|
||||
let (remaining, name) = lesser_block_begin("export")(context, input)?;
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let (remaining, _) = lesser_block_begin("export")(context, input)?;
|
||||
// https://orgmode.org/worg/org-syntax.html#Blocks claims that export blocks must have a single word for data but testing shows no data and multi-word data still parses as an export block.
|
||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||
let (remaining, export_type) = opt(map(
|
||||
tuple((space1, switch_word, peek(tuple((space0, line_ending))))),
|
||||
|(_, export_type, _)| export_type,
|
||||
))(remaining)?;
|
||||
let (remaining, parameters) =
|
||||
opt(map(tuple((space1, data)), |(_, parameters)| parameters))(remaining)?;
|
||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||
let lesser_block_end_specialized = lesser_block_end("export");
|
||||
let contexts = [
|
||||
@ -190,12 +227,8 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>(
|
||||
let parser_context = context.with_additional_node(&contexts[0]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||
let parameters = match parameters {
|
||||
Some((_ws, parameters)) => Some(parameters),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
||||
let (remaining, contents) = content(&parser_context, remaining)?;
|
||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||
|
||||
let source = get_consumed(input, remaining);
|
||||
@ -203,9 +236,10 @@ pub(crate) fn export_block<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
ExportBlock {
|
||||
source: source.into(),
|
||||
name: name.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
export_type: export_type.map(Into::<&str>::into),
|
||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
||||
contents: contents.into(),
|
||||
contents,
|
||||
},
|
||||
))
|
||||
}
|
||||
@ -215,9 +249,15 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, SrcBlock<'s>> {
|
||||
let (remaining, name) = lesser_block_begin("src")(context, input)?;
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let (remaining, _) = lesser_block_begin("src")(context, input)?;
|
||||
// https://orgmode.org/worg/org-syntax.html#Blocks claims that data is mandatory and must follow the LANGUAGE SWITCHES ARGUMENTS pattern but testing has shown that no data and incorrect data here will still parse to a src block.
|
||||
let (remaining, parameters) = opt(tuple((space1, data)))(remaining)?;
|
||||
let (remaining, language) =
|
||||
opt(map(tuple((space1, switch_word)), |(_, language)| language))(remaining)?;
|
||||
let (remaining, switches) = opt(src_switches)(remaining)?;
|
||||
let (remaining, parameters) = opt(map(tuple((space1, src_parameters)), |(_, parameters)| {
|
||||
parameters
|
||||
}))(remaining)?;
|
||||
let (remaining, _nl) = recognize(tuple((space0, line_ending)))(remaining)?;
|
||||
let lesser_block_end_specialized = lesser_block_end("src");
|
||||
let contexts = [
|
||||
@ -231,22 +271,42 @@ pub(crate) fn src_block<'b, 'g, 'r, 's>(
|
||||
let parser_context = context.with_additional_node(&contexts[0]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[2]);
|
||||
let parameters = match parameters {
|
||||
Some((_ws, parameters)) => Some(parameters),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let (remaining, contents) = parser_with_context!(text_until_exit)(&parser_context)(remaining)?;
|
||||
let (remaining, contents) = content(&parser_context, remaining)?;
|
||||
let (remaining, _end) = lesser_block_end_specialized(&parser_context, remaining)?;
|
||||
|
||||
let source = get_consumed(input, remaining);
|
||||
let (switches, number_lines, preserve_indent, retain_labels, use_labels, label_format) = {
|
||||
if let Some(switches) = switches {
|
||||
(
|
||||
if switches.source.len() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(switches.source)
|
||||
},
|
||||
switches.number_lines,
|
||||
switches.preserve_indent,
|
||||
switches.retain_labels,
|
||||
switches.use_labels,
|
||||
switches.label_format,
|
||||
)
|
||||
} else {
|
||||
(None, None, None, RetainLabels::Yes, true, None)
|
||||
}
|
||||
};
|
||||
Ok((
|
||||
remaining,
|
||||
SrcBlock {
|
||||
source: source.into(),
|
||||
name: name.into(),
|
||||
data: parameters.map(|parameters| Into::<&str>::into(parameters)),
|
||||
contents: contents.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
language: language.map(Into::<&str>::into),
|
||||
switches,
|
||||
parameters: parameters.map(Into::<&str>::into),
|
||||
number_lines,
|
||||
preserve_indent,
|
||||
retain_labels,
|
||||
use_labels,
|
||||
label_format,
|
||||
contents,
|
||||
},
|
||||
))
|
||||
}
|
||||
@ -309,3 +369,259 @@ fn _lesser_block_begin<'b, 'g, 'r, 's, 'c>(
|
||||
))(remaining)?;
|
||||
Ok((remaining, name))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ExampleSrcSwitches<'s> {
|
||||
source: &'s str,
|
||||
number_lines: Option<SwitchNumberLines>,
|
||||
retain_labels: RetainLabels,
|
||||
preserve_indent: Option<CharOffsetInLine>,
|
||||
use_labels: bool,
|
||||
label_format: Option<&'s str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SwitchState {
|
||||
Normal,
|
||||
NewLineNumber,
|
||||
ContinuedLineNumber,
|
||||
LabelFormat,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn src_parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
verify(
|
||||
recognize(many_till(anychar, peek(tuple((space0, line_ending))))),
|
||||
|parameters: &OrgSource<'_>| parameters.len() > 0,
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn src_switches<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ExampleSrcSwitches<'s>> {
|
||||
let (remaining, leading_spaces) = space1(input)?;
|
||||
let offset = Into::<&str>::into(leading_spaces).chars().count();
|
||||
let offset = CharOffsetInLine::try_from(offset)
|
||||
.expect("Character offset should fit in CharOffsetInLine");
|
||||
example_src_switches(true, offset)(remaining)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn example_switches<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ExampleSrcSwitches<'s>> {
|
||||
let (remaining, switches) = example_src_switches(false, 0)(input)?;
|
||||
Ok((remaining, switches))
|
||||
}
|
||||
|
||||
fn example_src_switches(
|
||||
stop_at_parameters: bool,
|
||||
additional_char_offset: CharOffsetInLine,
|
||||
) -> impl for<'s> Fn(OrgSource<'s>) -> Res<OrgSource<'s>, ExampleSrcSwitches<'s>> {
|
||||
move |input| _example_src_switches(input, stop_at_parameters, additional_char_offset)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn _example_src_switches<'s>(
|
||||
input: OrgSource<'s>,
|
||||
stop_at_parameters: bool,
|
||||
additional_char_offset: CharOffsetInLine,
|
||||
) -> Res<OrgSource<'s>, ExampleSrcSwitches<'s>> {
|
||||
let mut number_lines = None;
|
||||
let mut retain_labels = RetainLabels::Yes;
|
||||
let mut preserve_indent = None;
|
||||
let mut use_labels = true;
|
||||
let mut label_format = None;
|
||||
let mut saw_r = false;
|
||||
let mut matched_a_word = false;
|
||||
let mut remaining = input;
|
||||
let mut last_match_remaining = input;
|
||||
let mut state = SwitchState::Normal;
|
||||
'outer: loop {
|
||||
let (remain, word) = opt(switch_word)(remaining)?;
|
||||
let word = match word {
|
||||
Some(word) => word,
|
||||
None => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
let normalized_word = Into::<&str>::into(word);
|
||||
|
||||
loop {
|
||||
match (&state, normalized_word) {
|
||||
(SwitchState::Normal, "-n") => {
|
||||
state = SwitchState::NewLineNumber;
|
||||
}
|
||||
(SwitchState::Normal, "+n") => {
|
||||
state = SwitchState::ContinuedLineNumber;
|
||||
}
|
||||
(SwitchState::Normal, "-r") => {
|
||||
saw_r = true;
|
||||
use_labels = false;
|
||||
match retain_labels {
|
||||
RetainLabels::Yes => {
|
||||
retain_labels = RetainLabels::No;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
(SwitchState::Normal, "-l") => {
|
||||
state = SwitchState::LabelFormat;
|
||||
}
|
||||
(SwitchState::Normal, "-k") => {
|
||||
use_labels = false;
|
||||
let text_until_flag = input.get_until(word);
|
||||
let character_offset = Into::<&str>::into(text_until_flag).chars().count();
|
||||
let character_offset = CharOffsetInLine::try_from(character_offset)
|
||||
.expect("Character offset should fit in CharOffsetInLine");
|
||||
retain_labels = RetainLabels::Keep(character_offset + additional_char_offset);
|
||||
}
|
||||
(SwitchState::Normal, "-i") => {
|
||||
let text_until_flag = input.get_until(word);
|
||||
let character_offset = Into::<&str>::into(text_until_flag).chars().count();
|
||||
let character_offset = CharOffsetInLine::try_from(character_offset)
|
||||
.expect("Character offset should fit in CharOffsetInLine");
|
||||
preserve_indent = Some(character_offset + additional_char_offset);
|
||||
}
|
||||
(SwitchState::NewLineNumber, _) => {
|
||||
let val = normalized_word.parse::<LineNumber>();
|
||||
if let Ok(val) = val {
|
||||
if val < 0 {
|
||||
number_lines = Some(SwitchNumberLines::New(0));
|
||||
} else {
|
||||
// Note that this can result in a negative 1 if the val is originally 0.
|
||||
number_lines = Some(SwitchNumberLines::New(val - 1));
|
||||
}
|
||||
state = SwitchState::Normal;
|
||||
} else {
|
||||
number_lines = Some(SwitchNumberLines::New(0));
|
||||
state = SwitchState::Normal;
|
||||
continue; // Re-processes the word
|
||||
}
|
||||
}
|
||||
(SwitchState::ContinuedLineNumber, _) => {
|
||||
let val = normalized_word.parse::<LineNumber>();
|
||||
if let Ok(val) = val {
|
||||
if val < 0 {
|
||||
number_lines = Some(SwitchNumberLines::Continued(0));
|
||||
} else {
|
||||
// Note that this can result in a negative 1 if the val is originally 0.
|
||||
number_lines = Some(SwitchNumberLines::Continued(val - 1));
|
||||
}
|
||||
state = SwitchState::Normal;
|
||||
} else {
|
||||
number_lines = Some(SwitchNumberLines::Continued(0));
|
||||
state = SwitchState::Normal;
|
||||
continue; // Re-processes the word
|
||||
}
|
||||
}
|
||||
(SwitchState::LabelFormat, _) => {
|
||||
label_format = Some(normalized_word);
|
||||
state = SwitchState::Normal;
|
||||
}
|
||||
(SwitchState::Normal, _) if stop_at_parameters => {
|
||||
break 'outer;
|
||||
}
|
||||
(SwitchState::Normal, _) => {}
|
||||
};
|
||||
matched_a_word = true;
|
||||
remaining = remain;
|
||||
last_match_remaining = remain;
|
||||
|
||||
let (remain, divider) = opt(space1)(remaining)?;
|
||||
if divider.is_none() {
|
||||
break 'outer;
|
||||
}
|
||||
remaining = remain;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !matched_a_word {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError("No words."))));
|
||||
}
|
||||
let remaining = last_match_remaining;
|
||||
|
||||
let (remaining, _post_spaces) = opt(tuple((space0, peek(line_ending))))(remaining)?;
|
||||
let source = input.get_until(remaining);
|
||||
|
||||
// Handle state that didn't get processed because we ran out of words.
|
||||
match state {
|
||||
SwitchState::Normal => {}
|
||||
SwitchState::NewLineNumber => {
|
||||
number_lines = Some(SwitchNumberLines::New(0));
|
||||
}
|
||||
SwitchState::ContinuedLineNumber => {
|
||||
number_lines = Some(SwitchNumberLines::Continued(0));
|
||||
}
|
||||
SwitchState::LabelFormat => {}
|
||||
}
|
||||
|
||||
let retain_labels = match retain_labels {
|
||||
RetainLabels::Keep(_) if !saw_r => RetainLabels::Yes,
|
||||
_ => retain_labels,
|
||||
};
|
||||
|
||||
Ok((
|
||||
remaining,
|
||||
ExampleSrcSwitches {
|
||||
source: Into::<&str>::into(source),
|
||||
number_lines,
|
||||
retain_labels,
|
||||
preserve_indent,
|
||||
use_labels,
|
||||
label_format,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn switch_word<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
alt((
|
||||
map(
|
||||
tuple((tag(r#"""#), is_not("\"\r\n"), tag(r#"""#))),
|
||||
|(_, contents, _)| contents,
|
||||
),
|
||||
is_not(" \t\r\n"),
|
||||
))(input)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn content<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, String> {
|
||||
let mut ret = String::new();
|
||||
let mut remaining = input;
|
||||
let exit_matcher_parser = parser_with_context!(exit_matcher_parser)(context);
|
||||
loop {
|
||||
if exit_matcher_parser(remaining).is_ok() {
|
||||
break;
|
||||
}
|
||||
|
||||
let (remain, (pre_escape_whitespace, line)) = content_line(remaining)?;
|
||||
pre_escape_whitespace.map(|val| ret.push_str(Into::<&str>::into(val)));
|
||||
ret.push_str(line.into());
|
||||
remaining = remain;
|
||||
}
|
||||
|
||||
Ok((remaining, ret))
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
fn content_line<'s>(
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, (Option<OrgSource<'s>>, OrgSource<'s>)> {
|
||||
let (remaining, pre_escape_whitespace) = opt(map(
|
||||
tuple((
|
||||
recognize(tuple((
|
||||
space0,
|
||||
many_till(
|
||||
tag(","),
|
||||
peek(tuple((tag(","), alt((tag("#+"), tag("*")))))),
|
||||
),
|
||||
))),
|
||||
tag(","),
|
||||
)),
|
||||
|(pre_comma, _)| pre_comma,
|
||||
))(input)?;
|
||||
let (remaining, line_post_escape) = recognize(many_till(anychar, line_ending))(remaining)?;
|
||||
Ok((remaining, (pre_escape_whitespace, line_post_escape)))
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use crate::error::MyError;
|
||||
|
||||
pub(crate) type BracketDepth = i16;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub(crate) struct OrgSource<'s> {
|
||||
full_source: &'s str,
|
||||
start: usize,
|
||||
|
@ -2,19 +2,26 @@ use nom::branch::alt;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::recognize;
|
||||
use nom::combinator::verify;
|
||||
use nom::multi::many0;
|
||||
use nom::multi::many1;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::element_parser::detect_element;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::blank_line;
|
||||
use super::util::get_consumed;
|
||||
use super::util::get_has_affiliated_keyword;
|
||||
use super::util::get_name;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
use crate::context::ExitClass;
|
||||
use crate::context::ExitMatcherNode;
|
||||
use crate::context::HasAffiliatedKeywordInner;
|
||||
use crate::context::RefContext;
|
||||
use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::parser::object_parser::standard_set_object;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
@ -26,11 +33,19 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Paragraph<'s>> {
|
||||
let parser_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Gamma,
|
||||
exit_matcher: ¶graph_end,
|
||||
});
|
||||
let parser_context = context.with_additional_node(&parser_context);
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
let contexts = [
|
||||
ContextElement::HasAffiliatedKeyword(HasAffiliatedKeywordInner {
|
||||
start_after_affiliated_keywords: input,
|
||||
keywords: &affiliated_keywords,
|
||||
}),
|
||||
ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Gamma,
|
||||
exit_matcher: ¶graph_end,
|
||||
}),
|
||||
];
|
||||
let parser_context = context.with_additional_node(&contexts[0]);
|
||||
let parser_context = parser_context.with_additional_node(&contexts[1]);
|
||||
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
|
||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||
|
||||
@ -47,6 +62,7 @@ pub(crate) fn paragraph<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Paragraph {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
children,
|
||||
},
|
||||
))
|
||||
@ -57,10 +73,21 @@ fn paragraph_end<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, OrgSource<'s>> {
|
||||
let non_paragraph_element_matcher = parser_with_context!(detect_element(false))(context);
|
||||
let regular_end = recognize(tuple((start_of_line, many1(blank_line))))(input);
|
||||
if regular_end.is_ok() {
|
||||
return regular_end;
|
||||
}
|
||||
match get_has_affiliated_keyword(context) {
|
||||
Some(start_post_affiliated_keywords) if input == start_post_affiliated_keywords => {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"No exit due to affiliated keywords.",
|
||||
))));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Check to see if input is the start of a HasAffiliatedKeyword
|
||||
alt((
|
||||
recognize(tuple((start_of_line, many1(blank_line)))),
|
||||
recognize(non_paragraph_element_matcher),
|
||||
recognize(parser_with_context!(detect_element(false))(context)),
|
||||
eof,
|
||||
))(input)
|
||||
}
|
||||
|
@ -19,8 +19,10 @@ use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::element_parser::element;
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::object_parser::standard_set_object;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::get_name;
|
||||
use super::util::include_input;
|
||||
use super::util::indentation_level;
|
||||
use super::util::non_whitespace_character;
|
||||
@ -53,6 +55,7 @@ pub(crate) fn detect_plain_list<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, ()> {
|
||||
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||
if verify(
|
||||
tuple((
|
||||
start_of_line,
|
||||
@ -78,6 +81,8 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, PlainList<'s>> {
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
|
||||
let contexts = [
|
||||
ContextElement::Context("plain list"),
|
||||
ContextElement::ConsumeTrailingWhitespace(true),
|
||||
@ -150,6 +155,7 @@ pub(crate) fn plain_list<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
PlainList {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
list_type: first_item_list_type.expect("Plain lists require at least one element."),
|
||||
children: children.into_iter().map(|(_start, item)| item).collect(),
|
||||
},
|
||||
|
@ -120,7 +120,7 @@ fn node_property<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
NodeProperty {
|
||||
source: source.into(),
|
||||
name: Into::<&str>::into(name),
|
||||
property_name: Into::<&str>::into(name),
|
||||
value: None,
|
||||
},
|
||||
))
|
||||
@ -133,7 +133,7 @@ fn node_property<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
NodeProperty {
|
||||
source: source.into(),
|
||||
name: Into::<&str>::into(name),
|
||||
property_name: Into::<&str>::into(name),
|
||||
value: Some(value.into()),
|
||||
},
|
||||
))
|
||||
|
@ -13,10 +13,12 @@ use nom::multi::many1;
|
||||
use nom::multi::many_till;
|
||||
use nom::sequence::tuple;
|
||||
|
||||
use super::keyword::affiliated_keyword;
|
||||
use super::keyword::table_formula_keyword;
|
||||
use super::object_parser::table_cell_set_object;
|
||||
use super::org_source::OrgSource;
|
||||
use super::util::exit_matcher_parser;
|
||||
use super::util::get_name;
|
||||
use super::util::org_line_ending;
|
||||
use crate::context::parser_with_context;
|
||||
use crate::context::ContextElement;
|
||||
@ -38,6 +40,7 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
input: OrgSource<'s>,
|
||||
) -> Res<OrgSource<'s>, Table<'s>> {
|
||||
let (input, affiliated_keywords) = many0(affiliated_keyword)(input)?;
|
||||
start_of_line(input)?;
|
||||
peek(tuple((space0, tag("|"))))(input)?;
|
||||
|
||||
@ -68,6 +71,7 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>(
|
||||
remaining,
|
||||
Table {
|
||||
source: source.into(),
|
||||
name: get_name(&affiliated_keywords),
|
||||
formulas,
|
||||
children,
|
||||
},
|
||||
@ -76,6 +80,7 @@ pub(crate) fn org_mode_table<'b, 'g, 'r, 's>(
|
||||
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
|
||||
pub(crate) fn detect_table<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
|
||||
let (input, _) = many0(affiliated_keyword)(input)?;
|
||||
tuple((start_of_line, space0, tag("|")))(input)?;
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ use crate::error::CustomError;
|
||||
use crate::error::MyError;
|
||||
use crate::error::Res;
|
||||
use crate::types::IndentationLevel;
|
||||
use crate::types::Keyword;
|
||||
|
||||
pub(crate) const WORD_CONSTITUENT_CHARACTERS: &str =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
@ -275,3 +276,36 @@ pub(crate) fn indentation_level<'b, 'g, 'r, 's>(
|
||||
.sum();
|
||||
Ok((remaining, (indentation_level, leading_whitespace)))
|
||||
}
|
||||
|
||||
pub(crate) fn get_name<'s>(affiliated_keywords: &Vec<Keyword<'s>>) -> Option<&'s str> {
|
||||
let name_keyword = affiliated_keywords
|
||||
.iter()
|
||||
.filter(|kw| {
|
||||
kw.key.eq_ignore_ascii_case("name")
|
||||
|| kw.key.eq_ignore_ascii_case("source")
|
||||
|| kw.key.eq_ignore_ascii_case("tblname")
|
||||
|| kw.key.eq_ignore_ascii_case("resname")
|
||||
|| kw.key.eq_ignore_ascii_case("srcname")
|
||||
|| kw.key.eq_ignore_ascii_case("label")
|
||||
})
|
||||
.last();
|
||||
name_keyword.map(|kw| kw.value)
|
||||
}
|
||||
|
||||
pub(crate) fn get_has_affiliated_keyword<'b, 'g, 'r, 's>(
|
||||
context: RefContext<'b, 'g, 'r, 's>,
|
||||
) -> Option<OrgSource<'s>> {
|
||||
for context in context.iter() {
|
||||
match context {
|
||||
ContextElement::HasAffiliatedKeyword(inner) => {
|
||||
if !inner.keywords.is_empty() {
|
||||
return Some(inner.start_after_affiliated_keywords);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use super::StandardProperties;
|
||||
#[derive(Debug)]
|
||||
pub struct PlainList<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub list_type: PlainListType,
|
||||
pub children: Vec<PlainListItem<'s>>,
|
||||
}
|
||||
@ -46,19 +47,22 @@ pub enum CheckboxType {
|
||||
#[derive(Debug)]
|
||||
pub struct CenterBlock<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub children: Vec<Element<'s>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct QuoteBlock<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub children: Vec<Element<'s>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SpecialBlock<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub block_type: &'s str,
|
||||
pub parameters: Option<&'s str>,
|
||||
pub children: Vec<Element<'s>>,
|
||||
}
|
||||
@ -66,7 +70,8 @@ pub struct SpecialBlock<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct DynamicBlock<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub block_name: &'s str,
|
||||
pub parameters: Option<&'s str>,
|
||||
pub children: Vec<Element<'s>>,
|
||||
}
|
||||
@ -74,6 +79,7 @@ pub struct DynamicBlock<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct FootnoteDefinition<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub label: &'s str,
|
||||
pub children: Vec<Element<'s>>,
|
||||
}
|
||||
@ -81,7 +87,8 @@ pub struct FootnoteDefinition<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct Drawer<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub drawer_name: &'s str,
|
||||
pub children: Vec<Element<'s>>,
|
||||
}
|
||||
|
||||
@ -94,13 +101,14 @@ pub struct PropertyDrawer<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct NodeProperty<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: &'s str,
|
||||
pub property_name: &'s str,
|
||||
pub value: Option<&'s str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Table<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub formulas: Vec<Keyword<'s>>,
|
||||
pub children: Vec<TableRow<'s>>,
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use super::Timestamp;
|
||||
#[derive(Debug)]
|
||||
pub struct Paragraph<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub children: Vec<Object<'s>>,
|
||||
}
|
||||
|
||||
@ -24,7 +25,7 @@ pub struct TableCell<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct VerseBlock<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub data: Option<&'s str>,
|
||||
pub children: Vec<Object<'s>>,
|
||||
}
|
||||
@ -32,33 +33,55 @@ pub struct VerseBlock<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct CommentBlock<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: &'s str,
|
||||
pub data: Option<&'s str>,
|
||||
pub name: Option<&'s str>,
|
||||
pub contents: &'s str,
|
||||
}
|
||||
|
||||
pub type CharOffsetInLine = u16;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RetainLabels {
|
||||
No,
|
||||
Yes,
|
||||
/// When adding -k to the switches on an example or src block, the labels are kept in the source code and links will use line numbers.
|
||||
Keep(CharOffsetInLine),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExampleBlock<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: &'s str,
|
||||
pub data: Option<&'s str>,
|
||||
pub contents: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub switches: Option<&'s str>,
|
||||
pub number_lines: Option<SwitchNumberLines>,
|
||||
pub preserve_indent: Option<CharOffsetInLine>,
|
||||
pub retain_labels: RetainLabels,
|
||||
pub use_labels: bool,
|
||||
pub label_format: Option<&'s str>,
|
||||
pub contents: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExportBlock<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub export_type: Option<&'s str>,
|
||||
pub data: Option<&'s str>,
|
||||
pub contents: &'s str,
|
||||
pub contents: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SrcBlock<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: &'s str,
|
||||
pub data: Option<&'s str>,
|
||||
pub contents: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub language: Option<&'s str>,
|
||||
pub switches: Option<&'s str>,
|
||||
pub parameters: Option<&'s str>,
|
||||
pub number_lines: Option<SwitchNumberLines>,
|
||||
pub preserve_indent: Option<CharOffsetInLine>,
|
||||
pub retain_labels: RetainLabels,
|
||||
pub use_labels: bool,
|
||||
pub label_format: Option<&'s str>,
|
||||
pub contents: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -69,6 +92,7 @@ pub struct Clock<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct DiarySexp<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -82,16 +106,19 @@ pub struct Planning<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct FixedWidthArea<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HorizontalRule<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Keyword<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub key: &'s str,
|
||||
pub value: &'s str,
|
||||
}
|
||||
@ -99,6 +126,7 @@ pub struct Keyword<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct BabelCall<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
pub key: &'s str,
|
||||
pub value: &'s str,
|
||||
}
|
||||
@ -106,14 +134,30 @@ pub struct BabelCall<'s> {
|
||||
#[derive(Debug)]
|
||||
pub struct LatexEnvironment<'s> {
|
||||
pub source: &'s str,
|
||||
pub name: Option<&'s str>,
|
||||
}
|
||||
|
||||
/// A line number used in switches to lesser blocks.
|
||||
///
|
||||
/// This must be signed because emacs subtracts 1 from the actual value in the org-mode text, which makes a 0 turn into a -1.
|
||||
pub type LineNumber = isize;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SwitchNumberLines {
|
||||
New(LineNumber),
|
||||
Continued(LineNumber),
|
||||
}
|
||||
|
||||
impl<'s> Paragraph<'s> {
|
||||
/// Generate a paragraph of the passed in text with no additional properties.
|
||||
///
|
||||
/// This is used for elements that support an "empty" content like greater blocks.
|
||||
pub(crate) fn of_text(input: &'s str) -> Self {
|
||||
let mut objects = Vec::with_capacity(1);
|
||||
objects.push(Object::PlainText(PlainText { source: input }));
|
||||
Paragraph {
|
||||
source: input,
|
||||
name: None,
|
||||
children: objects,
|
||||
}
|
||||
}
|
||||
@ -223,3 +267,12 @@ impl<'s> Comment<'s> {
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> ExportBlock<'s> {
|
||||
/// Gets the export type capitalized.
|
||||
///
|
||||
/// Upstream Emacs Org-mode capitalizes the export type.
|
||||
pub fn get_export_type(&self) -> Option<String> {
|
||||
self.export_type.map(|s| s.to_uppercase())
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ pub use greater_element::Table;
|
||||
pub use greater_element::TableRow;
|
||||
pub use greater_element::TableRowType;
|
||||
pub use lesser_element::BabelCall;
|
||||
pub use lesser_element::CharOffsetInLine;
|
||||
pub use lesser_element::Clock;
|
||||
pub use lesser_element::Comment;
|
||||
pub use lesser_element::CommentBlock;
|
||||
@ -47,9 +48,12 @@ pub use lesser_element::FixedWidthArea;
|
||||
pub use lesser_element::HorizontalRule;
|
||||
pub use lesser_element::Keyword;
|
||||
pub use lesser_element::LatexEnvironment;
|
||||
pub use lesser_element::LineNumber;
|
||||
pub use lesser_element::Paragraph;
|
||||
pub use lesser_element::Planning;
|
||||
pub use lesser_element::RetainLabels;
|
||||
pub use lesser_element::SrcBlock;
|
||||
pub use lesser_element::SwitchNumberLines;
|
||||
pub use lesser_element::TableCell;
|
||||
pub use lesser_element::VerseBlock;
|
||||
pub use object::AngleLink;
|
||||
|
Loading…
x
Reference in New Issue
Block a user