Merge branch 'test_improvement'
This commit is contained in:
commit
31da2e970c
6
Makefile
6
Makefile
@ -24,12 +24,16 @@ clean:
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
> cargo test --lib
|
||||
> cargo test --lib --test test_loader
|
||||
|
||||
.PHONY: integrationtest
|
||||
integrationtest:
|
||||
> cargo test --no-fail-fast --test test_loader
|
||||
|
||||
.PHONY: unittest
|
||||
unittest:
|
||||
> cargo test --lib
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
> cargo run
|
||||
|
33
build.rs
33
build.rs
@ -22,17 +22,28 @@ fn main() {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
Err(_) => true,
|
||||
}).collect::<Result<Vec<_>, _>>().unwrap();
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap();
|
||||
for test in test_files {
|
||||
write_test(&mut test_file, &test);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
|
||||
let test_name = test.path().strip_prefix("org_mode_samples/").expect("Paths should be under org_mode_samples/").to_string_lossy().to_lowercase().strip_suffix(".org").expect("Should have .org extension").replace("/", "_");
|
||||
|
||||
let test_name = test
|
||||
.path()
|
||||
.strip_prefix("org_mode_samples/")
|
||||
.expect("Paths should be under org_mode_samples/")
|
||||
.to_string_lossy()
|
||||
.to_lowercase()
|
||||
.strip_suffix(".org")
|
||||
.expect("Should have .org extension")
|
||||
.replace("/", "_");
|
||||
|
||||
if let Some(reason) = is_expect_fail(test_name.as_str()) {
|
||||
write!(test_file, "#[ignore]\n").unwrap();
|
||||
}
|
||||
write!(
|
||||
test_file,
|
||||
include_str!("./tests/test_template"),
|
||||
@ -56,3 +67,17 @@ use organic::sexp;
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn is_expect_fail(name: &str) -> Option<&str> {
|
||||
match name {
|
||||
"drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."),
|
||||
"element_container_priority_drawer_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||
"element_container_priority_dynamic_block_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||
"element_container_priority_footnote_definition_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||
"element_container_priority_greater_block_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||
"element_container_priority_section_dynamic_block" => Some("Keyword needs to be implemented."),
|
||||
"exit_matcher_investigation_table_list" => Some("Table needs to be implemented."),
|
||||
"element_container_priority_readme" => Some("Table needs to be implemented."),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
27
scripts/run_integration_test.bash
Executable file
27
scripts/run_integration_test.bash
Executable file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
cd "$DIR/../"
|
||||
|
||||
samples_dir=$(readlink -f "org_mode_samples")
|
||||
|
||||
function get_test_names {
|
||||
for test_file in "$@"
|
||||
do
|
||||
if [ -e "$test_file" ]; then
|
||||
test_file_full_path=$(readlink -f "$test_file")
|
||||
relative_to_samples=$(realpath --relative-to "$samples_dir" "$test_file_full_path")
|
||||
without_extension="${relative_to_samples%.org}"
|
||||
echo "${without_extension/\//_}"
|
||||
else
|
||||
echo "$test_file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
get_test_names "$@" | while read test; do
|
||||
cargo test --no-fail-fast --test test_loader "$test"
|
||||
done
|
@ -1,4 +1,6 @@
|
||||
use super::sexp::Token;
|
||||
use super::util::assert_bounds;
|
||||
use super::util::assert_name;
|
||||
use crate::compare::util::get_offsets;
|
||||
use crate::parser::Comment;
|
||||
use crate::parser::Document;
|
||||
@ -18,6 +20,7 @@ use crate::DynamicBlock;
|
||||
pub struct DiffResult {
|
||||
status: DiffStatus,
|
||||
name: String,
|
||||
message: Option<String>,
|
||||
children: Vec<DiffResult>,
|
||||
}
|
||||
|
||||
@ -71,13 +74,11 @@ pub fn compare_document<'s>(
|
||||
rust: &'s Document<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children.first().ok_or("Should have at least one child.")?;
|
||||
let first_child_text = first_child.as_atom()?;
|
||||
if first_child_text != "org-data" {
|
||||
return Err("Document should correspond to an org-data cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(emacs, "org-data").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
// Skipping "org-data" and the first parameter which is often nil
|
||||
for (i, token) in children.iter().skip(2).enumerate() {
|
||||
@ -112,6 +113,7 @@ pub fn compare_document<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "document".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -122,29 +124,13 @@ fn compare_section<'s>(
|
||||
rust: &'s Section<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children.first().ok_or("Should have at least one child.")?;
|
||||
let first_child_text = first_child.as_atom()?;
|
||||
if first_child_text != "section" {
|
||||
return Err("Section should correspond to a section cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
let mut child_status = Vec::new();
|
||||
if assert_name(emacs, "section").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
@ -155,6 +141,7 @@ fn compare_section<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "section".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -165,29 +152,13 @@ fn compare_heading<'s>(
|
||||
rust: &'s Heading<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children.first().ok_or("Should have at least one child.")?;
|
||||
let first_child_text = first_child.as_atom()?;
|
||||
if first_child_text != "headline" {
|
||||
return Err("Heading should correspond to a headline cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(emacs, "headline").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
@ -205,6 +176,7 @@ fn compare_heading<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "heading".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -231,31 +203,13 @@ fn compare_paragraph<'s>(
|
||||
rust: &'s Paragraph<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children
|
||||
.first()
|
||||
.ok_or("Should have at least one child.")?
|
||||
.as_atom()?;
|
||||
if first_child != "paragraph" {
|
||||
return Err("Paragraph should correspond to a paragraph cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(emacs, "paragraph").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
@ -264,6 +218,7 @@ fn compare_paragraph<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "paragraph".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -274,31 +229,13 @@ fn compare_plain_list<'s>(
|
||||
rust: &'s PlainList<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children
|
||||
.first()
|
||||
.ok_or("Should have at least one child.")?
|
||||
.as_atom()?;
|
||||
if first_child != "plain-list" {
|
||||
return Err("Paragraph should correspond to a paragraph cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(emacs, "plain-list").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
@ -309,6 +246,7 @@ fn compare_plain_list<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "plain-list".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -319,31 +257,13 @@ fn compare_plain_list_item<'s>(
|
||||
rust: &'s PlainListItem<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children
|
||||
.first()
|
||||
.ok_or("Should have at least one child.")?
|
||||
.as_atom()?;
|
||||
if first_child != "item" {
|
||||
return Err("PlainListItem should correspond to an item cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(emacs, "item").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
@ -354,6 +274,7 @@ fn compare_plain_list_item<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "plain-list-item".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -364,43 +285,20 @@ fn compare_greater_block<'s>(
|
||||
rust: &'s GreaterBlock<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children
|
||||
.first()
|
||||
.ok_or("Should have at least one child.")?
|
||||
.as_atom()?;
|
||||
match rust.name.to_lowercase().as_str() {
|
||||
"center" => {
|
||||
if first_child != "center-block" {
|
||||
return Err(
|
||||
"Center greater blocks should correspond to a center-block cell.".into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
"quote" => {
|
||||
if first_child != "quote-block" {
|
||||
return Err("Quote greater blocks should correspond to a quote-block cell.".into());
|
||||
}
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(
|
||||
emacs,
|
||||
match rust.name.to_lowercase().as_str() {
|
||||
"center" => "center-block",
|
||||
"quote" => "quote-block",
|
||||
_ => todo!(),
|
||||
},
|
||||
).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
@ -411,6 +309,7 @@ fn compare_greater_block<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "greater-block".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -421,31 +320,13 @@ fn compare_dynamic_block<'s>(
|
||||
rust: &'s DynamicBlock<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children
|
||||
.first()
|
||||
.ok_or("Should have at least one child.")?
|
||||
.as_atom()?;
|
||||
if first_child != "dynamic-block" {
|
||||
return Err("Dynamic block should correspond to a dynamic-block cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(emacs, "dynamic-block").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
@ -456,6 +337,7 @@ fn compare_dynamic_block<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "dynamic-block".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -466,31 +348,13 @@ fn compare_footnote_definition<'s>(
|
||||
rust: &'s FootnoteDefinition<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children
|
||||
.first()
|
||||
.ok_or("Should have at least one child.")?
|
||||
.as_atom()?;
|
||||
if first_child != "footnote-definition" {
|
||||
return Err("Paragraph should correspond to a paragraph cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(emacs, "footnote-definition").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
@ -501,6 +365,7 @@ fn compare_footnote_definition<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "footnote-definition".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -511,37 +376,20 @@ fn compare_comment<'s>(
|
||||
rust: &'s Comment<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children
|
||||
.first()
|
||||
.ok_or("Should have at least one child.")?
|
||||
.as_atom()?;
|
||||
if first_child != "comment" {
|
||||
return Err("Comment should correspond to a comment cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(emacs, "comment").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "comment".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
@ -552,31 +400,13 @@ fn compare_drawer<'s>(
|
||||
rust: &'s Drawer<'s>,
|
||||
) -> Result<DiffResult, Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children
|
||||
.first()
|
||||
.ok_or("Should have at least one child.")?
|
||||
.as_atom()?;
|
||||
if first_child != "drawer" {
|
||||
return Err("Drawer should correspond to a drawer cell.".into());
|
||||
}
|
||||
let mut child_status = Vec::new();
|
||||
let mut this_status = DiffStatus::Good;
|
||||
if assert_name(emacs, "drawer").is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
if assert_bounds(source, emacs, rust).is_err() {
|
||||
this_status = DiffStatus::Bad;
|
||||
}
|
||||
|
||||
@ -587,6 +417,7 @@ fn compare_drawer<'s>(
|
||||
Ok(DiffResult {
|
||||
status: this_status,
|
||||
name: "drawer".to_owned(),
|
||||
message: None,
|
||||
children: child_status,
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::parser::Source;
|
||||
|
||||
use super::sexp::Token;
|
||||
|
||||
/// Check if the child string slice is a slice of the parent string slice.
|
||||
fn is_slice_of(parent: &str, child: &str) -> bool {
|
||||
let parent_start = parent.as_ptr() as usize;
|
||||
@ -19,3 +21,46 @@ pub fn get_offsets<'s, S: Source<'s>>(source: &'s str, rust_object: &'s S) -> (u
|
||||
let end = offset + rust_object_source.len();
|
||||
(offset, end)
|
||||
}
|
||||
|
||||
pub fn assert_name<'s>(emacs: &'s Token<'s>, name: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let first_child = children
|
||||
.first()
|
||||
.ok_or("Should have at least one child.")?
|
||||
.as_atom()?;
|
||||
if first_child != name {
|
||||
Err(format!(
|
||||
"Expected a {expected} cell, but found a {found} cell.",
|
||||
expected = name,
|
||||
found = first_child
|
||||
))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn assert_bounds<'s, S: Source<'s>>(
|
||||
source: &'s str,
|
||||
emacs: &'s Token<'s>,
|
||||
rust: &'s S,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let children = emacs.as_list()?;
|
||||
let attributes_child = children
|
||||
.iter()
|
||||
.nth(1)
|
||||
.ok_or("Should have an attributes child.")?;
|
||||
let attributes_map = attributes_child.as_map()?;
|
||||
let begin = attributes_map
|
||||
.get(":begin")
|
||||
.ok_or("Missing :begin attribute.")?
|
||||
.as_atom()?;
|
||||
let end = attributes_map
|
||||
.get(":end")
|
||||
.ok_or("Missing :end attribute.")?
|
||||
.as_atom()?;
|
||||
let (rust_begin, rust_end) = get_offsets(source, rust);
|
||||
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
|
||||
Err(format!("Rust bounds ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin + 1, rust_end = rust_end + 1, emacs_begin=begin, emacs_end=end))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use nom::bytes::complete::tag;
|
||||
use nom::bytes::complete::take_while;
|
||||
use nom::character::complete::line_ending;
|
||||
use nom::character::complete::space0;
|
||||
use nom::combinator::consumed;
|
||||
use nom::combinator::eof;
|
||||
use nom::combinator::recognize;
|
||||
use nom::multi::many_till;
|
||||
@ -17,6 +18,7 @@ use crate::parser::exiting::ExitClass;
|
||||
use crate::parser::parser_context::ContextElement;
|
||||
use crate::parser::parser_context::ExitMatcherNode;
|
||||
use crate::parser::parser_with_context::parser_with_context;
|
||||
use crate::parser::util::blank_line;
|
||||
use crate::parser::util::exit_matcher_parser;
|
||||
use crate::parser::util::get_consumed;
|
||||
use crate::parser::util::immediate_in_section;
|
||||
@ -24,6 +26,8 @@ use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting;
|
||||
use crate::parser::util::start_of_line;
|
||||
use crate::parser::util::WORD_CONSTITUENT_CHARACTERS;
|
||||
use crate::parser::Drawer;
|
||||
use crate::parser::Element;
|
||||
use crate::parser::Paragraph;
|
||||
|
||||
#[tracing::instrument(ret, level = "debug")]
|
||||
pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Drawer<'s>> {
|
||||
@ -51,8 +55,17 @@ pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
|
||||
|
||||
let element_matcher = parser_with_context!(element)(&parser_context);
|
||||
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
|
||||
let (remaining, (children, _exit_contents)) =
|
||||
many_till(element_matcher, exit_matcher)(remaining)?;
|
||||
let (remaining, children) = match consumed(many_till(blank_line, exit_matcher))(remaining) {
|
||||
Ok((remaining, (whitespace, (_children, _exit_contents)))) => (
|
||||
remaining,
|
||||
vec![Element::Paragraph(Paragraph::of_text(whitespace))],
|
||||
),
|
||||
Err(_) => {
|
||||
let (remaining, (children, _exit_contents)) =
|
||||
many_till(element_matcher, exit_matcher)(remaining)?;
|
||||
(remaining, children)
|
||||
}
|
||||
};
|
||||
let (remaining, _end) = drawer_end(&parser_context, remaining)?;
|
||||
|
||||
let (remaining, _trailing_ws) =
|
||||
|
@ -83,10 +83,18 @@ fn footnote_definition_end<'r, 's>(
|
||||
input: &'s str,
|
||||
) -> Res<&'s str, &'s str> {
|
||||
let start_of_line_matcher = parser_with_context!(start_of_line)(context);
|
||||
let footnote_definition_matcher = parser_with_context!(footnote_definition)(context);
|
||||
let allow_nesting_context =
|
||||
context.with_additional_node(ContextElement::Context("allow nesting footnotes"));
|
||||
let footnote_definition_matcher = parser_with_context!(footnote_definition)(
|
||||
if immediate_in_section(context, "footnote definition") {
|
||||
&allow_nesting_context
|
||||
} else {
|
||||
context
|
||||
},
|
||||
);
|
||||
let maybe_consume_trailing_whitespace_matcher =
|
||||
parser_with_context!(maybe_consume_trailing_whitespace)(context);
|
||||
alt((
|
||||
let (remaining, source) = alt((
|
||||
recognize(tuple((
|
||||
maybe_consume_trailing_whitespace_matcher,
|
||||
footnote_definition_matcher,
|
||||
@ -95,7 +103,9 @@ fn footnote_definition_end<'r, 's>(
|
||||
start_of_line_matcher,
|
||||
verify(many1(blank_line), |lines: &Vec<&str>| lines.len() >= 2),
|
||||
))),
|
||||
))(input)
|
||||
))(input)?;
|
||||
|
||||
Ok((remaining, source))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -35,11 +35,6 @@ pub fn greater_block<'r, 's>(
|
||||
input: &'s str,
|
||||
) -> Res<&'s str, GreaterBlock<'s>> {
|
||||
// TODO: Do I need to differentiate between different greater block types.
|
||||
if immediate_in_section(context, "greater block") {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"Cannot nest objects of the same element",
|
||||
))));
|
||||
}
|
||||
start_of_line(context, input)?;
|
||||
let (remaining, _leading_whitespace) = space0(input)?;
|
||||
// TODO: Not handling indentation before start of block
|
||||
@ -50,11 +45,21 @@ pub fn greater_block<'r, 's>(
|
||||
_ => true,
|
||||
}),
|
||||
))(remaining)?;
|
||||
let context_name = match name.to_lowercase().as_str() {
|
||||
"center" => "center block",
|
||||
"quote" => "quote block",
|
||||
_ => "greater block",
|
||||
};
|
||||
if immediate_in_section(context, context_name) {
|
||||
return Err(nom::Err::Error(CustomError::MyError(MyError(
|
||||
"Cannot nest objects of the same element",
|
||||
))));
|
||||
}
|
||||
let (remaining, parameters) = opt(tuple((space1, parameters)))(remaining)?;
|
||||
let (remaining, _nl) = line_ending(remaining)?;
|
||||
let parser_context = context
|
||||
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
|
||||
.with_additional_node(ContextElement::Context("greater block"))
|
||||
.with_additional_node(ContextElement::Context(context_name))
|
||||
.with_additional_node(ContextElement::GreaterBlock(name))
|
||||
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
|
||||
class: ExitClass::Alpha,
|
||||
|
@ -1,5 +1,5 @@
|
||||
:firstdrawer:
|
||||
:seconddrawer:
|
||||
foo
|
||||
:end:
|
||||
:drawername:
|
||||
|
||||
|
||||
:end:
|
||||
|
Loading…
x
Reference in New Issue
Block a user