Merge branch 'standard_properties'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded

This commit is contained in:
Tom Alexander 2023-09-23 21:12:40 -04:00
commit 6097e4df18
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
21 changed files with 1073 additions and 1020 deletions

File diff suppressed because it is too large Load Diff

474
src/compare/elisp_fact.rs Normal file
View File

@ -0,0 +1,474 @@
use std::borrow::Cow;
use crate::types::AngleLink;
use crate::types::BabelCall;
use crate::types::Bold;
use crate::types::Citation;
use crate::types::CitationReference;
use crate::types::Clock;
use crate::types::Code;
use crate::types::Comment;
use crate::types::CommentBlock;
use crate::types::DiarySexp;
use crate::types::Document;
use crate::types::Drawer;
use crate::types::DynamicBlock;
use crate::types::Element;
use crate::types::Entity;
use crate::types::ExampleBlock;
use crate::types::ExportBlock;
use crate::types::ExportSnippet;
use crate::types::FixedWidthArea;
use crate::types::FootnoteDefinition;
use crate::types::FootnoteReference;
use crate::types::GreaterBlock;
use crate::types::Heading;
use crate::types::HorizontalRule;
use crate::types::InlineBabelCall;
use crate::types::InlineSourceBlock;
use crate::types::Italic;
use crate::types::Keyword;
use crate::types::LatexEnvironment;
use crate::types::LatexFragment;
use crate::types::LineBreak;
use crate::types::NodeProperty;
use crate::types::Object;
use crate::types::OrgMacro;
use crate::types::Paragraph;
use crate::types::PlainLink;
use crate::types::PlainList;
use crate::types::PlainListItem;
use crate::types::PlainText;
use crate::types::Planning;
use crate::types::PropertyDrawer;
use crate::types::RadioLink;
use crate::types::RadioTarget;
use crate::types::RegularLink;
use crate::types::Section;
use crate::types::SrcBlock;
use crate::types::StatisticsCookie;
use crate::types::StrikeThrough;
use crate::types::Subscript;
use crate::types::Superscript;
use crate::types::Table;
use crate::types::TableCell;
use crate::types::TableRow;
use crate::types::Target;
use crate::types::Timestamp;
use crate::types::Underline;
use crate::types::Verbatim;
use crate::types::VerseBlock;
pub(crate) trait ElispFact<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str>;
}
pub(crate) trait GetElispFact<'s> {
fn get_elisp_fact(&'s self) -> &'s dyn ElispFact<'s>;
}
impl<'s, I: ElispFact<'s>> GetElispFact<'s> for I {
fn get_elisp_fact(&'s self) -> &'s dyn ElispFact<'s> {
self
}
}
impl<'s> GetElispFact<'s> for Element<'s> {
fn get_elisp_fact(&'s self) -> &'s dyn ElispFact<'s> {
match self {
Element::Paragraph(inner) => inner,
Element::PlainList(inner) => inner,
Element::GreaterBlock(inner) => inner,
Element::DynamicBlock(inner) => inner,
Element::FootnoteDefinition(inner) => inner,
Element::Comment(inner) => inner,
Element::Drawer(inner) => inner,
Element::PropertyDrawer(inner) => inner,
Element::Table(inner) => inner,
Element::VerseBlock(inner) => inner,
Element::CommentBlock(inner) => inner,
Element::ExampleBlock(inner) => inner,
Element::ExportBlock(inner) => inner,
Element::SrcBlock(inner) => inner,
Element::Clock(inner) => inner,
Element::DiarySexp(inner) => inner,
Element::Planning(inner) => inner,
Element::FixedWidthArea(inner) => inner,
Element::HorizontalRule(inner) => inner,
Element::Keyword(inner) => inner,
Element::BabelCall(inner) => inner,
Element::LatexEnvironment(inner) => inner,
}
}
}
impl<'s> GetElispFact<'s> for Object<'s> {
fn get_elisp_fact(&'s self) -> &'s dyn ElispFact<'s> {
match self {
Object::Bold(inner) => inner,
Object::Italic(inner) => inner,
Object::Underline(inner) => inner,
Object::StrikeThrough(inner) => inner,
Object::Code(inner) => inner,
Object::Verbatim(inner) => inner,
Object::PlainText(inner) => inner,
Object::RegularLink(inner) => inner,
Object::RadioLink(inner) => inner,
Object::RadioTarget(inner) => inner,
Object::PlainLink(inner) => inner,
Object::AngleLink(inner) => inner,
Object::OrgMacro(inner) => inner,
Object::Entity(inner) => inner,
Object::LatexFragment(inner) => inner,
Object::ExportSnippet(inner) => inner,
Object::FootnoteReference(inner) => inner,
Object::Citation(inner) => inner,
Object::CitationReference(inner) => inner,
Object::InlineBabelCall(inner) => inner,
Object::InlineSourceBlock(inner) => inner,
Object::LineBreak(inner) => inner,
Object::Target(inner) => inner,
Object::StatisticsCookie(inner) => inner,
Object::Subscript(inner) => inner,
Object::Superscript(inner) => inner,
Object::Timestamp(inner) => inner,
}
}
}
impl<'s> ElispFact<'s> for Document<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"org-data".into()
}
}
impl<'s> ElispFact<'s> for Section<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"section".into()
}
}
impl<'s> ElispFact<'s> for Heading<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"headline".into()
}
}
impl<'s> ElispFact<'s> for PlainList<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"plain-list".into()
}
}
impl<'s> ElispFact<'s> for PlainListItem<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"item".into()
}
}
impl<'s> ElispFact<'s> for GreaterBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
match self.name.to_lowercase().as_str() {
"center" => "center-block".into(),
"quote" => "quote-block".into(),
_ => "special-block".into(),
}
}
}
impl<'s> ElispFact<'s> for DynamicBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"dynamic-block".into()
}
}
impl<'s> ElispFact<'s> for FootnoteDefinition<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"footnote-definition".into()
}
}
impl<'s> ElispFact<'s> for Drawer<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"drawer".into()
}
}
impl<'s> ElispFact<'s> for PropertyDrawer<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"property-drawer".into()
}
}
impl<'s> ElispFact<'s> for NodeProperty<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"node-property".into()
}
}
impl<'s> ElispFact<'s> for Table<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"table".into()
}
}
impl<'s> ElispFact<'s> for TableRow<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"table-row".into()
}
}
impl<'s> ElispFact<'s> for Paragraph<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"paragraph".into()
}
}
impl<'s> ElispFact<'s> for TableCell<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"table-cell".into()
}
}
impl<'s> ElispFact<'s> for Comment<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"comment".into()
}
}
impl<'s> ElispFact<'s> for VerseBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"verse-block".into()
}
}
impl<'s> ElispFact<'s> for CommentBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"comment-block".into()
}
}
impl<'s> ElispFact<'s> for ExampleBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"example-block".into()
}
}
impl<'s> ElispFact<'s> for ExportBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"export-block".into()
}
}
impl<'s> ElispFact<'s> for SrcBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"src-block".into()
}
}
impl<'s> ElispFact<'s> for Clock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"clock".into()
}
}
impl<'s> ElispFact<'s> for DiarySexp<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"diary-sexp".into()
}
}
impl<'s> ElispFact<'s> for Planning<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"planning".into()
}
}
impl<'s> ElispFact<'s> for FixedWidthArea<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"fixed-width".into()
}
}
impl<'s> ElispFact<'s> for HorizontalRule<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"horizontal-rule".into()
}
}
impl<'s> ElispFact<'s> for Keyword<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"keyword".into()
}
}
impl<'s> ElispFact<'s> for BabelCall<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"babel-call".into()
}
}
impl<'s> ElispFact<'s> for LatexEnvironment<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"latex-environment".into()
}
}
impl<'s> ElispFact<'s> for Bold<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"bold".into()
}
}
impl<'s> ElispFact<'s> for Italic<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"italic".into()
}
}
impl<'s> ElispFact<'s> for Underline<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"underline".into()
}
}
impl<'s> ElispFact<'s> for StrikeThrough<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"strike-through".into()
}
}
impl<'s> ElispFact<'s> for Code<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"code".into()
}
}
impl<'s> ElispFact<'s> for Verbatim<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"verbatim".into()
}
}
impl<'s> ElispFact<'s> for RegularLink<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"link".into()
}
}
impl<'s> ElispFact<'s> for RadioLink<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"link".into()
}
}
impl<'s> ElispFact<'s> for RadioTarget<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"radio-target".into()
}
}
impl<'s> ElispFact<'s> for PlainLink<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"link".into()
}
}
impl<'s> ElispFact<'s> for AngleLink<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"link".into()
}
}
impl<'s> ElispFact<'s> for OrgMacro<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"macro".into()
}
}
impl<'s> ElispFact<'s> for Entity<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"entity".into()
}
}
impl<'s> ElispFact<'s> for LatexFragment<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"latex-fragment".into()
}
}
impl<'s> ElispFact<'s> for ExportSnippet<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"export-snippet".into()
}
}
impl<'s> ElispFact<'s> for FootnoteReference<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"footnote-reference".into()
}
}
impl<'s> ElispFact<'s> for Citation<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"citation".into()
}
}
impl<'s> ElispFact<'s> for CitationReference<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"citation-reference".into()
}
}
impl<'s> ElispFact<'s> for InlineBabelCall<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"inline-babel-call".into()
}
}
impl<'s> ElispFact<'s> for InlineSourceBlock<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"inline-src-block".into()
}
}
impl<'s> ElispFact<'s> for LineBreak<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"line-break".into()
}
}
impl<'s> ElispFact<'s> for Target<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"target".into()
}
}
impl<'s> ElispFact<'s> for StatisticsCookie<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"statistics-cookie".into()
}
}
impl<'s> ElispFact<'s> for Subscript<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"subscript".into()
}
}
impl<'s> ElispFact<'s> for Superscript<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"superscript".into()
}
}
impl<'s> ElispFact<'s> for Timestamp<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
"timestamp".into()
}
}
impl<'s> ElispFact<'s> for PlainText<'s> {
fn get_elisp_name(&'s self) -> Cow<'s, str> {
// plain text from upstream emacs does not actually have a name but this is included here to make rendering the status diff easier.
"plain-text".into()
}
}

View File

@ -1,5 +1,6 @@
mod compare; mod compare;
mod diff; mod diff;
mod elisp_fact;
mod parse; mod parse;
mod sexp; mod sexp;
mod util; mod util;

View File

@ -1,5 +1,7 @@
use super::elisp_fact::GetElispFact;
use super::sexp::Token; use super::sexp::Token;
use crate::types::Source; use crate::types::GetStandardProperties;
use crate::types::StandardProperties;
/// Check if the child string slice is a slice of the parent string slice. /// Check if the child string slice is a slice of the parent string slice.
fn is_slice_of(parent: &str, child: &str) -> bool { fn is_slice_of(parent: &str, child: &str) -> bool {
@ -10,21 +12,38 @@ fn is_slice_of(parent: &str, child: &str) -> bool {
child_start >= parent_start && child_end <= parent_end child_start >= parent_start && child_end <= parent_end
} }
/// Get the offset into source that the rust object exists at. /// Get the byte offset into source that the rust object exists at.
/// ///
/// These offsets are zero-based unlike the elisp ones. /// These offsets are zero-based unlike the elisp ones.
fn get_offsets<'s, S: Source<'s>>(source: &'s str, rust_object: &'s S) -> (usize, usize) { fn get_rust_byte_offsets<'s, S: StandardProperties<'s> + ?Sized>(
let rust_object_source = rust_object.get_source(); original_document: &'s str,
assert!(is_slice_of(source, rust_object_source)); rust_ast_node: &'s S,
let offset = rust_object_source.as_ptr() as usize - source.as_ptr() as usize; ) -> (usize, usize) {
let rust_object_source = rust_ast_node.get_source();
debug_assert!(is_slice_of(original_document, rust_object_source));
let offset = rust_object_source.as_ptr() as usize - original_document.as_ptr() as usize;
let end = offset + rust_object_source.len(); let end = offset + rust_object_source.len();
(offset, end) (offset, end)
} }
pub(crate) fn assert_name<'s>( pub(crate) fn compare_standard_properties<
's,
S: GetStandardProperties<'s> + GetElispFact<'s> + ?Sized,
>(
original_document: &'s str,
emacs: &'s Token<'s>, emacs: &'s Token<'s>,
name: &str, rust: &'s S,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
assert_name(emacs, rust.get_elisp_fact().get_elisp_name())?;
assert_bounds(original_document, emacs, rust.get_standard_properties())?;
Ok(())
}
pub(crate) fn assert_name<'s, S: AsRef<str>>(
emacs: &'s Token<'s>,
name: S,
) -> Result<(), Box<dyn std::error::Error>> {
let name = name.as_ref();
let children = emacs.as_list()?; let children = emacs.as_list()?;
let first_child = children let first_child = children
.first() .first()
@ -32,7 +51,7 @@ pub(crate) fn assert_name<'s>(
.as_atom()?; .as_atom()?;
if first_child != name { if first_child != name {
Err(format!( Err(format!(
"Expected a {expected} cell, but found a {found} cell.", "AST node name mismatch. Expected a (rust) {expected} cell, but found a (emacs) {found} cell.",
expected = name, expected = name,
found = first_child found = first_child
))?; ))?;
@ -40,30 +59,33 @@ pub(crate) fn assert_name<'s>(
Ok(()) Ok(())
} }
pub(crate) fn assert_bounds<'s, S: Source<'s>>( /// Assert that the character ranges defined by upstream org-mode's :standard-properties match the slices in Organic's StandardProperties.
source: &'s str, ///
/// This does **not** handle plain text because plain text is a special case.
pub(crate) fn assert_bounds<'s, S: StandardProperties<'s> + ?Sized>(
original_document: &'s str,
emacs: &'s Token<'s>, emacs: &'s Token<'s>,
rust: &'s S, rust: &'s S,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let standard_properties = get_standard_properties(emacs)?; let standard_properties = get_emacs_standard_properties(emacs)?; // 1-based
let (begin, end) = ( let (begin, end) = (
standard_properties standard_properties
.begin .begin
.ok_or("Token should have a begin.")?, .ok_or("Token should have a begin.")?,
standard_properties.end.ok_or("Token should have an end.")?, standard_properties.end.ok_or("Token should have an end.")?,
); );
let (rust_begin, rust_end) = get_offsets(source, rust); let (rust_begin, rust_end) = get_rust_byte_offsets(original_document, rust); // 0-based
let rust_begin_char_offset = (&source[..rust_begin]).chars().count(); let rust_begin_char_offset = (&original_document[..rust_begin]).chars().count() + 1; // 1-based
let rust_end_char_offset = let rust_end_char_offset =
rust_begin_char_offset + (&source[rust_begin..rust_end]).chars().count(); rust_begin_char_offset + (&original_document[rust_begin..rust_end]).chars().count(); // 1-based
if (rust_begin_char_offset + 1) != begin || (rust_end_char_offset + 1) != end { if rust_begin_char_offset != begin || rust_end_char_offset != end {
Err(format!("Rust bounds (in chars) ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin_char_offset + 1, rust_end = rust_end_char_offset + 1, emacs_begin=begin, emacs_end=end))?; Err(format!("Rust bounds (in chars) ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin_char_offset, rust_end = rust_end_char_offset, emacs_begin=begin, emacs_end=end))?;
} }
Ok(()) Ok(())
} }
struct StandardProperties { struct EmacsStandardProperties {
begin: Option<usize>, begin: Option<usize>,
#[allow(dead_code)] #[allow(dead_code)]
post_affiliated: Option<usize>, post_affiliated: Option<usize>,
@ -76,9 +98,9 @@ struct StandardProperties {
post_blank: Option<usize>, post_blank: Option<usize>,
} }
fn get_standard_properties<'s>( fn get_emacs_standard_properties<'s>(
emacs: &'s Token<'s>, emacs: &'s Token<'s>,
) -> Result<StandardProperties, Box<dyn std::error::Error>> { ) -> Result<EmacsStandardProperties, Box<dyn std::error::Error>> {
let children = emacs.as_list()?; let children = emacs.as_list()?;
let attributes_child = children let attributes_child = children
.iter() .iter()
@ -97,7 +119,7 @@ fn get_standard_properties<'s>(
let contents_end = maybe_token_to_usize(std_props.next())?; let contents_end = maybe_token_to_usize(std_props.next())?;
let end = maybe_token_to_usize(std_props.next())?; let end = maybe_token_to_usize(std_props.next())?;
let post_blank = maybe_token_to_usize(std_props.next())?; let post_blank = maybe_token_to_usize(std_props.next())?;
StandardProperties { EmacsStandardProperties {
begin, begin,
post_affiliated, post_affiliated,
contents_begin, contents_begin,
@ -116,7 +138,7 @@ fn get_standard_properties<'s>(
maybe_token_to_usize(attributes_map.get(":post-blank").map(|token| *token))?; maybe_token_to_usize(attributes_map.get(":post-blank").map(|token| *token))?;
let post_affiliated = let post_affiliated =
maybe_token_to_usize(attributes_map.get(":post-affiliated").map(|token| *token))?; maybe_token_to_usize(attributes_map.get(":post-affiliated").map(|token| *token))?;
StandardProperties { EmacsStandardProperties {
begin, begin,
post_affiliated, post_affiliated,
contents_begin, contents_begin,

View File

@ -6,6 +6,7 @@ pub(crate) struct List<'parent, T> {
parent: Link<'parent, T>, parent: Link<'parent, T>,
} }
// TODO: Should I be defining a lifetime for T in the generics here? Ref: https://quinedot.github.io/rust-learning/dyn-elision-advanced.html#iteraction-with-type-aliases
type Link<'parent, T> = Option<&'parent List<'parent, T>>; type Link<'parent, T> = Option<&'parent List<'parent, T>>;
impl<'parent, T> List<'parent, T> { impl<'parent, T> List<'parent, T> {

View File

@ -187,7 +187,7 @@ mod tests {
use crate::context::List; use crate::context::List;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::types::Element; use crate::types::Element;
use crate::types::Source; use crate::types::GetStandardProperties;
#[test] #[test]
fn citation_simple() { fn citation_simple() {
@ -202,7 +202,10 @@ mod tests {
_ => panic!("Should be a paragraph!"), _ => panic!("Should be a paragraph!"),
}; };
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(first_paragraph.get_source(), "[cite:@foo]"); assert_eq!(
first_paragraph.get_standard_properties().get_source(),
"[cite:@foo]"
);
assert_eq!(first_paragraph.children.len(), 1); assert_eq!(first_paragraph.children.len(), 1);
assert_eq!( assert_eq!(
first_paragraph first_paragraph

View File

@ -129,7 +129,7 @@ mod tests {
use crate::context::Context; use crate::context::Context;
use crate::context::GlobalSettings; use crate::context::GlobalSettings;
use crate::context::List; use crate::context::List;
use crate::types::Source; use crate::types::GetStandardProperties;
#[test] #[test]
fn two_paragraphs() { fn two_paragraphs() {
@ -150,13 +150,17 @@ line footnote.",
footnote_definition_matcher(remaining).expect("Parse second footnote_definition."); footnote_definition_matcher(remaining).expect("Parse second footnote_definition.");
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!( assert_eq!(
first_footnote_definition.get_source(), first_footnote_definition
.get_standard_properties()
.get_source(),
"[fn:1] A footnote. "[fn:1] A footnote.
" "
); );
assert_eq!( assert_eq!(
second_footnote_definition.get_source(), second_footnote_definition
.get_standard_properties()
.get_source(),
"[fn:2] A multi- "[fn:2] A multi-
line footnote." line footnote."
@ -181,7 +185,9 @@ not in the footnote.",
footnote_definition_matcher(input).expect("Parse first footnote_definition"); footnote_definition_matcher(input).expect("Parse first footnote_definition");
assert_eq!(Into::<&str>::into(remaining), "not in the footnote."); assert_eq!(Into::<&str>::into(remaining), "not in the footnote.");
assert_eq!( assert_eq!(
first_footnote_definition.get_source(), first_footnote_definition
.get_standard_properties()
.get_source(),
"[fn:2] A multi- "[fn:2] A multi-
line footnote. line footnote.

View File

@ -24,6 +24,7 @@ use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::start_of_line; use crate::parser::util::start_of_line;
use crate::types::BabelCall;
use crate::types::Keyword; use crate::types::Keyword;
const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [ const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&'static str; 13] = [
@ -103,8 +104,16 @@ pub(crate) fn affiliated_keyword<'b, 'g, 'r, 's>(
pub(crate) fn babel_call_keyword<'b, 'g, 'r, 's>( pub(crate) fn babel_call_keyword<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>, _context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Keyword<'s>> { ) -> Res<OrgSource<'s>, BabelCall<'s>> {
filtered_keyword(babel_call_key)(input) let (remaining, kw) = filtered_keyword(babel_call_key)(input)?;
Ok((
remaining,
BabelCall {
source: kw.source,
key: kw.key,
value: kw.value,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@ -74,7 +74,7 @@ mod tests {
use crate::context::List; use crate::context::List;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::parser::org_source::OrgSource; use crate::parser::org_source::OrgSource;
use crate::types::Source; use crate::types::GetStandardProperties;
#[test] #[test]
fn two_paragraphs() { fn two_paragraphs() {
@ -87,7 +87,13 @@ mod tests {
let (remaining, second_paragraph) = let (remaining, second_paragraph) =
paragraph_matcher(remaining).expect("Parse second paragraph."); paragraph_matcher(remaining).expect("Parse second paragraph.");
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(first_paragraph.get_source(), "foo bar baz\n\n"); assert_eq!(
assert_eq!(second_paragraph.get_source(), "lorem ipsum"); first_paragraph.get_standard_properties().get_source(),
"foo bar baz\n\n"
);
assert_eq!(
second_paragraph.get_standard_properties().get_source(),
"lorem ipsum"
);
} }
} }

View File

@ -445,7 +445,7 @@ mod tests {
use crate::context::Context; use crate::context::Context;
use crate::context::GlobalSettings; use crate::context::GlobalSettings;
use crate::context::List; use crate::context::List;
use crate::types::Source; use crate::types::GetStandardProperties;
#[test] #[test]
fn plain_list_item_empty() { fn plain_list_item_empty() {
@ -456,7 +456,7 @@ mod tests {
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context); let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
let (remaining, result) = plain_list_item_matcher(input).unwrap(); let (remaining, result) = plain_list_item_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.source, "1."); assert_eq!(result.get_standard_properties().get_source(), "1.");
} }
#[test] #[test]
@ -468,7 +468,7 @@ mod tests {
let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context); let plain_list_item_matcher = parser_with_context!(plain_list_item)(&initial_context);
let (remaining, result) = plain_list_item_matcher(input).unwrap(); let (remaining, result) = plain_list_item_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.source, "1. foo"); assert_eq!(result.get_standard_properties().get_source(), "1. foo");
} }
#[test] #[test]
@ -480,7 +480,7 @@ mod tests {
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context); let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
let (remaining, result) = plain_list_matcher(input).unwrap(); let (remaining, result) = plain_list_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.source, "1."); assert_eq!(result.get_standard_properties().get_source(), "1.");
} }
#[test] #[test]
@ -492,7 +492,7 @@ mod tests {
let plain_list_matcher = parser_with_context!(plain_list)(&initial_context); let plain_list_matcher = parser_with_context!(plain_list)(&initial_context);
let (remaining, result) = plain_list_matcher(input).unwrap(); let (remaining, result) = plain_list_matcher(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.source, "1. foo"); assert_eq!(result.get_standard_properties().get_source(), "1. foo");
} }
#[test] #[test]
@ -539,7 +539,7 @@ mod tests {
plain_list_matcher(input).expect("Should parse the plain list successfully."); plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(Into::<&str>::into(remaining), " ipsum\n"); assert_eq!(Into::<&str>::into(remaining), " ipsum\n");
assert_eq!( assert_eq!(
result.get_source(), result.get_standard_properties().get_source(),
r#"1. foo r#"1. foo
2. bar 2. bar
baz baz
@ -567,7 +567,7 @@ baz"#,
plain_list_matcher(input).expect("Should parse the plain list successfully."); plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(Into::<&str>::into(remaining), "baz"); assert_eq!(Into::<&str>::into(remaining), "baz");
assert_eq!( assert_eq!(
result.get_source(), result.get_standard_properties().get_source(),
r#"1. foo r#"1. foo
1. bar 1. bar
@ -600,7 +600,7 @@ dolar"#,
plain_list_matcher(input).expect("Should parse the plain list successfully."); plain_list_matcher(input).expect("Should parse the plain list successfully.");
assert_eq!(Into::<&str>::into(remaining), "dolar"); assert_eq!(Into::<&str>::into(remaining), "dolar");
assert_eq!( assert_eq!(
result.get_source(), result.get_standard_properties().get_source(),
r#"1. foo r#"1. foo
bar bar

View File

@ -146,7 +146,7 @@ mod tests {
use crate::context::GlobalSettings; use crate::context::GlobalSettings;
use crate::context::List; use crate::context::List;
use crate::parser::object_parser::detect_standard_set_object_sans_plain_text; use crate::parser::object_parser::detect_standard_set_object_sans_plain_text;
use crate::types::Source; use crate::types::GetStandardProperties;
#[test] #[test]
fn plain_text_simple() { fn plain_text_simple() {
@ -159,6 +159,9 @@ mod tests {
))(&initial_context); ))(&initial_context);
let (remaining, result) = map(plain_text_matcher, Object::PlainText)(input).unwrap(); let (remaining, result) = map(plain_text_matcher, Object::PlainText)(input).unwrap();
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(result.get_source(), Into::<&str>::into(input)); assert_eq!(
result.get_standard_properties().get_source(),
Into::<&str>::into(input)
);
} }
} }

View File

@ -151,8 +151,8 @@ mod tests {
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::types::Bold; use crate::types::Bold;
use crate::types::Element; use crate::types::Element;
use crate::types::GetStandardProperties;
use crate::types::PlainText; use crate::types::PlainText;
use crate::types::Source;
#[test] #[test]
fn plain_text_radio_target() { fn plain_text_radio_target() {
@ -172,7 +172,10 @@ mod tests {
_ => panic!("Should be a paragraph!"), _ => panic!("Should be a paragraph!"),
}; };
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(first_paragraph.get_source(), "foo bar baz"); assert_eq!(
first_paragraph.get_standard_properties().get_source(),
"foo bar baz"
);
assert_eq!(first_paragraph.children.len(), 3); assert_eq!(first_paragraph.children.len(), 3);
assert_eq!( assert_eq!(
first_paragraph first_paragraph
@ -208,7 +211,10 @@ mod tests {
_ => panic!("Should be a paragraph!"), _ => panic!("Should be a paragraph!"),
}; };
assert_eq!(Into::<&str>::into(remaining), ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(first_paragraph.get_source(), "foo *bar* baz"); assert_eq!(
first_paragraph.get_standard_properties().get_source(),
"foo *bar* baz"
);
assert_eq!(first_paragraph.children.len(), 3); assert_eq!(first_paragraph.children.len(), 3);
assert_eq!( assert_eq!(
first_paragraph first_paragraph

View File

@ -1,6 +1,7 @@
use super::Element; use super::Element;
use super::GetStandardProperties;
use super::Object; use super::Object;
use super::Source; use super::StandardProperties;
pub type PriorityCookie = u8; pub type PriorityCookie = u8;
pub type HeadlineLevel = u16; pub type HeadlineLevel = u16;
@ -43,28 +44,28 @@ pub enum TodoKeywordType {
Done, Done,
} }
impl<'s> Source<'s> for Document<'s> { impl<'s> GetStandardProperties<'s> for DocumentElement<'s> {
fn get_source(&'s self) -> &'s str { fn get_standard_properties(&'s self) -> &'s dyn StandardProperties {
self.source
}
}
impl<'s> Source<'s> for DocumentElement<'s> {
fn get_source(&'s self) -> &'s str {
match self { match self {
DocumentElement::Heading(obj) => obj.source, DocumentElement::Heading(inner) => inner,
DocumentElement::Section(obj) => obj.source, DocumentElement::Section(inner) => inner,
} }
} }
} }
impl<'s> Source<'s> for Section<'s> { impl<'s> StandardProperties<'s> for Document<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Heading<'s> { impl<'s> StandardProperties<'s> for Section<'s> {
fn get_source(&'s self) -> &'s str {
self.source
}
}
impl<'s> StandardProperties<'s> for Heading<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }

View File

@ -4,6 +4,7 @@ use super::greater_element::GreaterBlock;
use super::greater_element::PlainList; use super::greater_element::PlainList;
use super::greater_element::PropertyDrawer; use super::greater_element::PropertyDrawer;
use super::greater_element::Table; use super::greater_element::Table;
use super::lesser_element::BabelCall;
use super::lesser_element::Clock; use super::lesser_element::Clock;
use super::lesser_element::Comment; use super::lesser_element::Comment;
use super::lesser_element::CommentBlock; use super::lesser_element::CommentBlock;
@ -19,8 +20,9 @@ use super::lesser_element::Planning;
use super::lesser_element::SrcBlock; use super::lesser_element::SrcBlock;
use super::lesser_element::VerseBlock; use super::lesser_element::VerseBlock;
use super::Drawer; use super::Drawer;
use super::GetStandardProperties;
use super::SetSource; use super::SetSource;
use super::Source; use super::StandardProperties;
#[derive(Debug)] #[derive(Debug)]
pub enum Element<'s> { pub enum Element<'s> {
@ -44,39 +46,10 @@ pub enum Element<'s> {
FixedWidthArea(FixedWidthArea<'s>), FixedWidthArea(FixedWidthArea<'s>),
HorizontalRule(HorizontalRule<'s>), HorizontalRule(HorizontalRule<'s>),
Keyword(Keyword<'s>), Keyword(Keyword<'s>),
BabelCall(Keyword<'s>), BabelCall(BabelCall<'s>),
LatexEnvironment(LatexEnvironment<'s>), LatexEnvironment(LatexEnvironment<'s>),
} }
impl<'s> Source<'s> for Element<'s> {
fn get_source(&'s self) -> &'s str {
match self {
Element::Paragraph(obj) => obj.get_source(),
Element::PlainList(obj) => obj.get_source(),
Element::GreaterBlock(obj) => obj.get_source(),
Element::DynamicBlock(obj) => obj.get_source(),
Element::FootnoteDefinition(obj) => obj.get_source(),
Element::Comment(obj) => obj.get_source(),
Element::Drawer(obj) => obj.get_source(),
Element::PropertyDrawer(obj) => obj.get_source(),
Element::Table(obj) => obj.get_source(),
Element::VerseBlock(obj) => obj.get_source(),
Element::CommentBlock(obj) => obj.get_source(),
Element::ExampleBlock(obj) => obj.get_source(),
Element::ExportBlock(obj) => obj.get_source(),
Element::SrcBlock(obj) => obj.get_source(),
Element::Clock(obj) => obj.get_source(),
Element::DiarySexp(obj) => obj.get_source(),
Element::Planning(obj) => obj.get_source(),
Element::FixedWidthArea(obj) => obj.get_source(),
Element::HorizontalRule(obj) => obj.get_source(),
Element::Keyword(obj) => obj.get_source(),
Element::BabelCall(obj) => obj.get_source(),
Element::LatexEnvironment(obj) => obj.get_source(),
}
}
}
impl<'s> SetSource<'s> for Element<'s> { impl<'s> SetSource<'s> for Element<'s> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn set_source(&mut self, source: &'s str) { fn set_source(&mut self, source: &'s str) {
@ -106,3 +79,32 @@ impl<'s> SetSource<'s> for Element<'s> {
} }
} }
} }
impl<'s> GetStandardProperties<'s> for Element<'s> {
fn get_standard_properties(&'s self) -> &'s dyn StandardProperties {
match self {
Element::Paragraph(inner) => inner,
Element::PlainList(inner) => inner,
Element::GreaterBlock(inner) => inner,
Element::DynamicBlock(inner) => inner,
Element::FootnoteDefinition(inner) => inner,
Element::Comment(inner) => inner,
Element::Drawer(inner) => inner,
Element::PropertyDrawer(inner) => inner,
Element::Table(inner) => inner,
Element::VerseBlock(inner) => inner,
Element::CommentBlock(inner) => inner,
Element::ExampleBlock(inner) => inner,
Element::ExportBlock(inner) => inner,
Element::SrcBlock(inner) => inner,
Element::Clock(inner) => inner,
Element::DiarySexp(inner) => inner,
Element::Planning(inner) => inner,
Element::FixedWidthArea(inner) => inner,
Element::HorizontalRule(inner) => inner,
Element::Keyword(inner) => inner,
Element::BabelCall(inner) => inner,
Element::LatexEnvironment(inner) => inner,
}
}
}

View File

@ -0,0 +1,12 @@
use super::StandardProperties;
pub trait GetStandardProperties<'s> {
// TODO: Can I eliminate this dynamic dispatch, perhaps using nominal generic structs? Low prioritiy since this is not used during parsing.
fn get_standard_properties(&'s self) -> &'s dyn StandardProperties;
}
impl<'s, I: StandardProperties<'s>> GetStandardProperties<'s> for I {
fn get_standard_properties(&'s self) -> &'s dyn StandardProperties {
self
}
}

View File

@ -2,7 +2,7 @@ use super::element::Element;
use super::lesser_element::TableCell; use super::lesser_element::TableCell;
use super::Keyword; use super::Keyword;
use super::Object; use super::Object;
use super::Source; use super::StandardProperties;
#[derive(Debug)] #[derive(Debug)]
pub struct PlainList<'s> { pub struct PlainList<'s> {
@ -85,61 +85,61 @@ pub struct TableRow<'s> {
pub children: Vec<TableCell<'s>>, pub children: Vec<TableCell<'s>>,
} }
impl<'s> Source<'s> for PlainList<'s> { impl<'s> StandardProperties<'s> for PlainList<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for PlainListItem<'s> { impl<'s> StandardProperties<'s> for PlainListItem<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for GreaterBlock<'s> { impl<'s> StandardProperties<'s> for GreaterBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for DynamicBlock<'s> { impl<'s> StandardProperties<'s> for DynamicBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for FootnoteDefinition<'s> { impl<'s> StandardProperties<'s> for FootnoteDefinition<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Drawer<'s> { impl<'s> StandardProperties<'s> for Drawer<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for PropertyDrawer<'s> { impl<'s> StandardProperties<'s> for PropertyDrawer<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for NodeProperty<'s> { impl<'s> StandardProperties<'s> for NodeProperty<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Table<'s> { impl<'s> StandardProperties<'s> for Table<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for TableRow<'s> { impl<'s> StandardProperties<'s> for TableRow<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }

View File

@ -1,6 +1,6 @@
use super::object::Object; use super::object::Object;
use super::PlainText; use super::PlainText;
use super::Source; use super::StandardProperties;
#[derive(Debug)] #[derive(Debug)]
pub struct Paragraph<'s> { pub struct Paragraph<'s> {
@ -91,6 +91,13 @@ pub struct Keyword<'s> {
pub value: &'s str, pub value: &'s str,
} }
#[derive(Debug)]
pub struct BabelCall<'s> {
pub source: &'s str,
pub key: &'s str,
pub value: &'s str,
}
#[derive(Debug)] #[derive(Debug)]
pub struct LatexEnvironment<'s> { pub struct LatexEnvironment<'s> {
pub source: &'s str, pub source: &'s str,
@ -107,87 +114,93 @@ impl<'s> Paragraph<'s> {
} }
} }
impl<'s> Source<'s> for Paragraph<'s> { impl<'s> StandardProperties<'s> for Paragraph<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for TableCell<'s> { impl<'s> StandardProperties<'s> for TableCell<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Comment<'s> { impl<'s> StandardProperties<'s> for Comment<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for VerseBlock<'s> { impl<'s> StandardProperties<'s> for VerseBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for CommentBlock<'s> { impl<'s> StandardProperties<'s> for CommentBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for ExampleBlock<'s> { impl<'s> StandardProperties<'s> for ExampleBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for ExportBlock<'s> { impl<'s> StandardProperties<'s> for ExportBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for SrcBlock<'s> { impl<'s> StandardProperties<'s> for SrcBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Clock<'s> { impl<'s> StandardProperties<'s> for Clock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for DiarySexp<'s> { impl<'s> StandardProperties<'s> for DiarySexp<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Planning<'s> { impl<'s> StandardProperties<'s> for Planning<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for FixedWidthArea<'s> { impl<'s> StandardProperties<'s> for FixedWidthArea<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for HorizontalRule<'s> { impl<'s> StandardProperties<'s> for HorizontalRule<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Keyword<'s> { impl<'s> StandardProperties<'s> for Keyword<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for LatexEnvironment<'s> { impl<'s> StandardProperties<'s> for BabelCall<'s> {
fn get_source(&'s self) -> &'s str {
self.source
}
}
impl<'s> StandardProperties<'s> for LatexEnvironment<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }

View File

@ -1,9 +1,11 @@
mod document; mod document;
mod element; mod element;
mod get_standard_properties;
mod greater_element; mod greater_element;
mod lesser_element; mod lesser_element;
mod object; mod object;
mod source; mod source;
mod standard_properties;
pub use document::Document; pub use document::Document;
pub use document::DocumentElement; pub use document::DocumentElement;
pub use document::Heading; pub use document::Heading;
@ -12,6 +14,7 @@ pub use document::PriorityCookie;
pub use document::Section; pub use document::Section;
pub use document::TodoKeywordType; pub use document::TodoKeywordType;
pub use element::Element; pub use element::Element;
pub use get_standard_properties::GetStandardProperties;
pub use greater_element::CheckboxType; pub use greater_element::CheckboxType;
pub use greater_element::Drawer; pub use greater_element::Drawer;
pub use greater_element::DynamicBlock; pub use greater_element::DynamicBlock;
@ -24,6 +27,7 @@ pub use greater_element::PlainListItem;
pub use greater_element::PropertyDrawer; pub use greater_element::PropertyDrawer;
pub use greater_element::Table; pub use greater_element::Table;
pub use greater_element::TableRow; pub use greater_element::TableRow;
pub use lesser_element::BabelCall;
pub use lesser_element::Clock; pub use lesser_element::Clock;
pub use lesser_element::Comment; pub use lesser_element::Comment;
pub use lesser_element::CommentBlock; pub use lesser_element::CommentBlock;
@ -68,4 +72,4 @@ pub use object::Timestamp;
pub use object::Underline; pub use object::Underline;
pub use object::Verbatim; pub use object::Verbatim;
pub(crate) use source::SetSource; pub(crate) use source::SetSource;
pub use source::Source; pub use standard_properties::StandardProperties;

View File

@ -1,4 +1,5 @@
use super::Source; use super::GetStandardProperties;
use super::StandardProperties;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Object<'s> { pub enum Object<'s> {
@ -185,197 +186,197 @@ pub struct Timestamp<'s> {
pub source: &'s str, pub source: &'s str,
} }
impl<'s> Source<'s> for Object<'s> { impl<'s> GetStandardProperties<'s> for Object<'s> {
fn get_source(&'s self) -> &'s str { fn get_standard_properties(&'s self) -> &'s dyn StandardProperties {
match self { match self {
Object::Bold(obj) => obj.source, Object::Bold(inner) => inner,
Object::Italic(obj) => obj.source, Object::Italic(inner) => inner,
Object::Underline(obj) => obj.source, Object::Underline(inner) => inner,
Object::StrikeThrough(obj) => obj.source, Object::StrikeThrough(inner) => inner,
Object::Code(obj) => obj.source, Object::Code(inner) => inner,
Object::Verbatim(obj) => obj.source, Object::Verbatim(inner) => inner,
Object::PlainText(obj) => obj.source, Object::PlainText(inner) => inner,
Object::RegularLink(obj) => obj.source, Object::RegularLink(inner) => inner,
Object::RadioLink(obj) => obj.source, Object::RadioLink(inner) => inner,
Object::RadioTarget(obj) => obj.source, Object::RadioTarget(inner) => inner,
Object::PlainLink(obj) => obj.source, Object::PlainLink(inner) => inner,
Object::AngleLink(obj) => obj.source, Object::AngleLink(inner) => inner,
Object::OrgMacro(obj) => obj.source, Object::OrgMacro(inner) => inner,
Object::Entity(obj) => obj.source, Object::Entity(inner) => inner,
Object::LatexFragment(obj) => obj.source, Object::LatexFragment(inner) => inner,
Object::ExportSnippet(obj) => obj.source, Object::ExportSnippet(inner) => inner,
Object::FootnoteReference(obj) => obj.source, Object::FootnoteReference(inner) => inner,
Object::Citation(obj) => obj.source, Object::Citation(inner) => inner,
Object::CitationReference(obj) => obj.source, Object::CitationReference(inner) => inner,
Object::InlineBabelCall(obj) => obj.source, Object::InlineBabelCall(inner) => inner,
Object::InlineSourceBlock(obj) => obj.source, Object::InlineSourceBlock(inner) => inner,
Object::LineBreak(obj) => obj.source, Object::LineBreak(inner) => inner,
Object::Target(obj) => obj.source, Object::Target(inner) => inner,
Object::Timestamp(obj) => obj.source, Object::StatisticsCookie(inner) => inner,
Object::StatisticsCookie(obj) => obj.source, Object::Subscript(inner) => inner,
Object::Subscript(obj) => obj.source, Object::Superscript(inner) => inner,
Object::Superscript(obj) => obj.source, Object::Timestamp(inner) => inner,
} }
} }
} }
impl<'s> Source<'s> for Bold<'s> { impl<'s> StandardProperties<'s> for Bold<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Italic<'s> { impl<'s> StandardProperties<'s> for Italic<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Underline<'s> { impl<'s> StandardProperties<'s> for Underline<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for StrikeThrough<'s> { impl<'s> StandardProperties<'s> for StrikeThrough<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Code<'s> { impl<'s> StandardProperties<'s> for Code<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Verbatim<'s> { impl<'s> StandardProperties<'s> for Verbatim<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for RegularLink<'s> { impl<'s> StandardProperties<'s> for RegularLink<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for RadioLink<'s> { impl<'s> StandardProperties<'s> for RadioLink<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for RadioTarget<'s> { impl<'s> StandardProperties<'s> for RadioTarget<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for PlainLink<'s> { impl<'s> StandardProperties<'s> for PlainLink<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for AngleLink<'s> { impl<'s> StandardProperties<'s> for AngleLink<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for OrgMacro<'s> { impl<'s> StandardProperties<'s> for OrgMacro<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Entity<'s> { impl<'s> StandardProperties<'s> for Entity<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for LatexFragment<'s> { impl<'s> StandardProperties<'s> for LatexFragment<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for ExportSnippet<'s> { impl<'s> StandardProperties<'s> for ExportSnippet<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for FootnoteReference<'s> { impl<'s> StandardProperties<'s> for FootnoteReference<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Citation<'s> { impl<'s> StandardProperties<'s> for Citation<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for CitationReference<'s> { impl<'s> StandardProperties<'s> for CitationReference<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for InlineBabelCall<'s> { impl<'s> StandardProperties<'s> for InlineBabelCall<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for InlineSourceBlock<'s> { impl<'s> StandardProperties<'s> for InlineSourceBlock<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for LineBreak<'s> { impl<'s> StandardProperties<'s> for LineBreak<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Target<'s> { impl<'s> StandardProperties<'s> for Target<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for StatisticsCookie<'s> { impl<'s> StandardProperties<'s> for StatisticsCookie<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Subscript<'s> { impl<'s> StandardProperties<'s> for Subscript<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Superscript<'s> { impl<'s> StandardProperties<'s> for Superscript<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for Timestamp<'s> { impl<'s> StandardProperties<'s> for Timestamp<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }
} }
impl<'s> Source<'s> for PlainText<'s> { impl<'s> StandardProperties<'s> for PlainText<'s> {
fn get_source(&'s self) -> &'s str { fn get_source(&'s self) -> &'s str {
self.source self.source
} }

View File

@ -1,6 +1,3 @@
pub trait Source<'s> {
fn get_source(&'s self) -> &'s str;
}
pub(crate) trait SetSource<'s> { pub(crate) trait SetSource<'s> {
fn set_source(&mut self, source: &'s str); fn set_source(&mut self, source: &'s str);
} }

View File

@ -0,0 +1,58 @@
// TODO: What is an anonymous AST node and how can I trigger one?
pub trait StandardProperties<'s> {
/// Get the slice of the entire AST node.
///
/// This corresponds to :begin to :end in upstream org-mode's standard properties.
fn get_source(&'s self) -> &'s str;
// Get the slice of the AST node's contents.
//
// This corresponds to :contents-begin to :contents-end
// fn get_contents(&'s self) -> &'s str;
}
// TODO: Write some debugging code to alert when any of the unknown fields below are non-nil in our test data so we can see what these fields represent.
// Order of upstream org-mode's standard properties array:
//
// :begin :post-affiliated :contents-begin :contents-end :end :post-blank :secondary :mode :granularity :cached :org-element--cache-sync-key :robust-begin :robust-end :true-level :buffer :deferred :structure :parent
//
// Per-field notes: (Leading character: 'X' for not going to include, 'Y' for going to include but not included yet ("Yes"), 'D' for already included ("Done"), '?' for undecided)
//
// D :begin - Number of characters (NOT bytes!) since the beginning of the file.
//
// ? :post-affiliated - ?
//
// Y :contents-begin - Number of characters (NOT bytes!) since the beginning of the file.
//
// Y :contents-end - Number of characters (NOT bytes!) since the beginning of the file.
//
// D :end - Number of characters (NOT bytes!) since the beginning of the file.
//
// Y :post-blank - Number of characters after :contents-end but before :end. This is the trailing whitespace.
//
// X :secondary - List of properties that may contain AST nodes. This will be important to reference for implementing TokenIter properly, but I see no value in including this in the StandardProperties trait since which properties contain AST nodes will be self-evident in the struct definition.
//
// ? :mode - ?
//
// ? :granularity - ?
//
// X :cached - ? Based on the name, I'm guessing this is a runtime-optimization rather than something relevant to export from a parser, so (unless I'm wrong about the purpose) I see no reason to include this.
//
// X :org-element--cache-sync-key - ? Based on the name, I'm guessing this is a runtime-optimization rather than something relevant to export from a parser, so (unless I'm wrong about the purpose) I see no reason to include this.
//
// ? :robust-begin - ? uhh what? What makes this begin/end "robust" and the others not? I have no idea.
//
// ? :robust-end - ? uhh what? What makes this begin/end "robust" and the others not? I have no idea.
//
// ? :true-level - This seems to correspond to the REAL star count for headlines (as opposed to the headline level we set for when "odd" is enabled instead of the default "oddeven"). This is great information to have, but is this a "standard" property? Does anything other than headlines have this set? I don't know, so I need to investigate. If it is headline-specific then we will not be including this in the StandardProperties trait even though it is in the :standard-properties array in org-mode.
//
// X :buffer - This is the Emacs buffer name containing the org-mode document. This seems more like a runtime thing than something we would want to export from our parser so this will not be included.
//
// X :deferred - Seems to be a runtime optimization about only calculating some properties when requested.
//
// ? :structure - ?
//
// X :parent - Some weird numeric reference to the containing object. Since we output a tree structure, I do not see any value in including this, especially considering the back-references would be a nightmare in rust.
// Special case: Plain text. Plain text counts :begin and :end from the start of the text (so :begin is always 0 AFAICT) and instead of including the full set of standard properties, it only includes :begin, :end, and :parent.