Merge branch 'keyword_constants'
This commit is contained in:
		
						commit
						a4381e5e39
					
				| @ -1,15 +1,27 @@ | ||||
| use super::global_settings::EntityDefinition; | ||||
| 
 | ||||
| pub(crate) const DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS: [&str; 1] = ["CAPTION"]; | ||||
| /// Keywords that contain the standard set of objects (excluding footnote references).
 | ||||
| ///
 | ||||
| /// Corresponds to org-element-parsed-keywords elisp variable.
 | ||||
| pub(crate) const ORG_ELEMENT_PARSED_KEYWORDS: [&str; 1] = ["CAPTION"]; | ||||
| 
 | ||||
| pub(crate) const DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS: [&str; 2] = ["CAPTION", "RESULTS"]; | ||||
| /// Keywords that can have a secondary value in square brackets.
 | ||||
| ///
 | ||||
| /// Corresponds to org-element-dual-keywords elisp variable.
 | ||||
| pub(crate) const ORG_ELEMENT_DUAL_KEYWORDS: [&str; 2] = ["CAPTION", "RESULTS"]; | ||||
| 
 | ||||
| pub(crate) const DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS: [&str; 13] = [ | ||||
| /// Keywords that can be affiliated with an element.
 | ||||
| ///
 | ||||
| /// Corresponds to org-element-affiliated-keywords elisp variable.
 | ||||
| pub(crate) const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&str; 13] = [ | ||||
|     "CAPTION", "DATA", "HEADER", "HEADERS", "LABEL", "NAME", "PLOT", "RESNAME", "RESULT", | ||||
|     "RESULTS", "SOURCE", "SRCNAME", "TBLNAME", | ||||
| ]; | ||||
| 
 | ||||
| pub(crate) const DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST: [(&str, &str); 8] = [ | ||||
| /// Mapping of keyword names.
 | ||||
| ///
 | ||||
| /// Corresponds to org-element-keyword-translation-alist elisp variable.
 | ||||
| pub(crate) const ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST: [(&str, &str); 8] = [ | ||||
|     ("DATA", "NAME"), | ||||
|     ("LABEL", "NAME"), | ||||
|     ("RESNAME", "NAME"), | ||||
|  | ||||
| @ -12,7 +12,6 @@ use crate::error::CustomError; | ||||
| use crate::error::Res; | ||||
| use crate::parser::OrgSource; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub(crate) enum ContextElement<'r, 's> { | ||||
|     /// Stores a parser that indicates that children should exit upon matching an exit matcher.
 | ||||
|     ExitMatcherNode(ExitMatcherNode<'r>), | ||||
| @ -34,15 +33,6 @@ pub(crate) struct ExitMatcherNode<'r> { | ||||
|     pub(crate) class: ExitClass, | ||||
| } | ||||
| 
 | ||||
| impl<'r> std::fmt::Debug for ExitMatcherNode<'r> { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         let mut formatter = f.debug_struct("ExitMatcherNode"); | ||||
|         formatter.field("class", &self.class.to_string()); | ||||
|         formatter.finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub(crate) struct Context<'g, 'r, 's> { | ||||
|     global_settings: &'g GlobalSettings<'g, 's>, | ||||
|     tree: List<'r, &'r ContextElement<'r, 's>>, | ||||
|  | ||||
| @ -1,13 +1,7 @@ | ||||
| #[derive(Debug, Copy, Clone)] | ||||
| #[derive(Copy, Clone)] | ||||
| pub(crate) enum ExitClass { | ||||
|     Document = 1, | ||||
|     Alpha = 2, | ||||
|     Beta = 3, | ||||
|     Gamma = 4, | ||||
| } | ||||
| 
 | ||||
| impl std::fmt::Display for ExitClass { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||
|         write!(f, "{:?}", self) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,17 +1,16 @@ | ||||
| use std::fmt::Debug; | ||||
| use std::path::PathBuf; | ||||
| 
 | ||||
| #[cfg(any(feature = "compare", feature = "foreign_document_test"))] | ||||
| pub trait FileAccessInterface: Sync + Debug { | ||||
| pub trait FileAccessInterface: Sync { | ||||
|     fn read_file(&self, path: &str) -> Result<String, std::io::Error>; | ||||
| } | ||||
| 
 | ||||
| #[cfg(not(any(feature = "compare", feature = "foreign_document_test")))] | ||||
| pub trait FileAccessInterface: Debug { | ||||
| pub trait FileAccessInterface { | ||||
|     fn read_file(&self, path: &str) -> Result<String, std::io::Error>; | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| #[derive(Clone)] | ||||
| pub struct LocalFileAccessInterface { | ||||
|     pub working_directory: Option<PathBuf>, | ||||
| } | ||||
|  | ||||
| @ -5,16 +5,12 @@ use super::constants::DEFAULT_ORG_ENTITIES; | ||||
| use super::constants::DEFAULT_ORG_LINK_PARAMETERS; | ||||
| use super::FileAccessInterface; | ||||
| use super::LocalFileAccessInterface; | ||||
| use crate::context::constants::DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS; | ||||
| use crate::context::constants::DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS; | ||||
| use crate::context::constants::DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST; | ||||
| use crate::context::constants::DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS; | ||||
| use crate::types::IndentationLevel; | ||||
| use crate::types::Object; | ||||
| 
 | ||||
| // TODO: Ultimately, I think we'll need most of this: https://orgmode.org/manual/In_002dbuffer-Settings.html
 | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| #[derive(Clone)] | ||||
| pub struct GlobalSettings<'g, 's> { | ||||
|     pub radio_targets: Vec<&'g Vec<Object<'s>>>, | ||||
|     pub file_access: &'g dyn FileAccessInterface, | ||||
| @ -58,26 +54,6 @@ pub struct GlobalSettings<'g, 's> { | ||||
|     ///
 | ||||
|     /// Corresponds to org-entities elisp variable.
 | ||||
|     pub entities: &'g [EntityDefinition<'s>], | ||||
| 
 | ||||
|     /// Keywords that contain the standard set of objects (excluding footnote references).
 | ||||
|     ///
 | ||||
|     /// Corresponds to org-element-parsed-keywords elisp variable.
 | ||||
|     pub element_parsed_keywords: &'g [&'s str], | ||||
| 
 | ||||
|     /// Keywords that can have a secondary value in square brackets.
 | ||||
|     ///
 | ||||
|     /// Corresponds to org-element-dual-keywords elisp variable.
 | ||||
|     pub element_dual_keywords: &'g [&'s str], | ||||
| 
 | ||||
|     /// Keywords that can be affiliated with an element.
 | ||||
|     ///
 | ||||
|     /// Corresponds to org-element-affiliated-keywords elisp variable.
 | ||||
|     pub element_affiliated_keywords: &'g [&'s str], | ||||
| 
 | ||||
|     /// Mapping of keyword names.
 | ||||
|     ///
 | ||||
|     /// Corresponds to org-element-keyword-translation-alist elisp variable.
 | ||||
|     pub element_keyword_translation_alist: &'g [(&'s str, &'s str)], | ||||
| } | ||||
| 
 | ||||
| pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8; | ||||
| @ -112,10 +88,6 @@ impl<'g, 's> GlobalSettings<'g, 's> { | ||||
|             link_parameters: &DEFAULT_ORG_LINK_PARAMETERS, | ||||
|             link_templates: BTreeMap::new(), | ||||
|             entities: &DEFAULT_ORG_ENTITIES, | ||||
|             element_parsed_keywords: &DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS, | ||||
|             element_dual_keywords: &DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS, | ||||
|             element_affiliated_keywords: &DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS, | ||||
|             element_keyword_translation_alist: &DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -126,7 +98,7 @@ impl<'g, 's> Default for GlobalSettings<'g, 's> { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone, PartialEq, Default)] | ||||
| #[derive(Clone, PartialEq, Default)] | ||||
| pub enum HeadlineLevelFilter { | ||||
|     Odd, | ||||
| 
 | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| use crate::error::Res; | ||||
| use crate::parser::OrgSource; | ||||
| 
 | ||||
| mod constants; | ||||
| pub(crate) mod constants; | ||||
| #[allow(clippy::module_inception)] | ||||
| mod context; | ||||
| mod exiting; | ||||
|  | ||||
| @ -19,29 +19,27 @@ use super::object_parser::standard_set_object; | ||||
| use super::util::confine_context; | ||||
| use super::OrgSource; | ||||
| use crate::context::bind_context; | ||||
| use crate::context::constants::ORG_ELEMENT_DUAL_KEYWORDS; | ||||
| use crate::context::constants::ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST; | ||||
| use crate::context::constants::ORG_ELEMENT_PARSED_KEYWORDS; | ||||
| use crate::context::Context; | ||||
| use crate::context::ContextElement; | ||||
| use crate::context::GlobalSettings; | ||||
| use crate::context::List; | ||||
| use crate::context::RefContext; | ||||
| use crate::error::Res; | ||||
| use crate::types::AffiliatedKeywordValue; | ||||
| use crate::types::AffiliatedKeywords; | ||||
| use crate::types::Keyword; | ||||
| 
 | ||||
| #[cfg_attr(
 | ||||
|     feature = "tracing", | ||||
|     tracing::instrument(ret, level = "debug", skip(context)) | ||||
| )] | ||||
| pub(crate) fn affiliated_keywords<'b, 'g, 'r, 's>( | ||||
|     context: RefContext<'b, 'g, 'r, 's>, | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| pub(crate) fn affiliated_keywords<'s>( | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, Vec<Keyword<'s>>> { | ||||
|     let mut ret = Vec::new(); | ||||
|     let mut remaining = input; | ||||
| 
 | ||||
|     loop { | ||||
|         let result = affiliated_keyword(context, remaining); | ||||
|         let result = affiliated_keyword(remaining); | ||||
|         match result { | ||||
|             Ok((remain, kw)) => { | ||||
|                 remaining = remain; | ||||
| @ -65,8 +63,8 @@ where | ||||
| { | ||||
|     let mut ret = BTreeMap::new(); | ||||
|     for kw in input { | ||||
|         let translated_name = translate_name(global_settings, kw.key); | ||||
|         let keyword_type = identify_keyword_type(global_settings, translated_name.as_str()); | ||||
|         let translated_name = translate_name(kw.key); | ||||
|         let keyword_type = identify_keyword_type(translated_name.as_str()); | ||||
|         match keyword_type { | ||||
|             AffiliatedKeywordType::SingleString => { | ||||
|                 ret.insert( | ||||
| @ -151,12 +149,12 @@ where | ||||
|     AffiliatedKeywords { keywords: ret } | ||||
| } | ||||
| 
 | ||||
| fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s str) -> String { | ||||
| fn translate_name(name: &str) -> String { | ||||
|     let name_until_optval = name | ||||
|         .split_once('[') | ||||
|         .map(|(before, _after)| before) | ||||
|         .unwrap_or(name); | ||||
|     for (src, dst) in global_settings.element_keyword_translation_alist { | ||||
|     for (src, dst) in ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST { | ||||
|         if name_until_optval.eq_ignore_ascii_case(src) { | ||||
|             return dst.to_lowercase(); | ||||
|         } | ||||
| @ -171,20 +169,15 @@ enum AffiliatedKeywordType { | ||||
|     ObjectTree, | ||||
| } | ||||
| 
 | ||||
| fn identify_keyword_type<'g, 's>( | ||||
|     global_settings: &'g GlobalSettings<'g, 's>, | ||||
|     name: &'s str, | ||||
| ) -> AffiliatedKeywordType { | ||||
| fn identify_keyword_type(name: &str) -> AffiliatedKeywordType { | ||||
|     let is_multiple = ["CAPTION", "HEADER"] | ||||
|         .into_iter() | ||||
|         .any(|candidate| name.eq_ignore_ascii_case(candidate)) | ||||
|         || name.to_lowercase().starts_with("attr_"); | ||||
|     let is_parsed = global_settings | ||||
|         .element_parsed_keywords | ||||
|     let is_parsed = ORG_ELEMENT_PARSED_KEYWORDS | ||||
|         .iter() | ||||
|         .any(|candidate| name.eq_ignore_ascii_case(candidate)); | ||||
|     let can_have_optval = global_settings | ||||
|         .element_dual_keywords | ||||
|     let can_have_optval = ORG_ELEMENT_DUAL_KEYWORDS | ||||
|         .iter() | ||||
|         .any(|candidate| name.eq_ignore_ascii_case(candidate)); | ||||
|     match (is_multiple, is_parsed, can_have_optval) { | ||||
|  | ||||
| @ -59,8 +59,7 @@ fn _element<'b, 'g, 'r, 's>( | ||||
| ) -> Res<OrgSource<'s>, Element<'s>> { | ||||
|     #[cfg(feature = "event_count")] | ||||
|     record_event(EventType::ElementStart, input); | ||||
|     let (post_affiliated_keywords_input, affiliated_keywords) = | ||||
|         affiliated_keywords(context, input)?; | ||||
|     let (post_affiliated_keywords_input, affiliated_keywords) = affiliated_keywords(input)?; | ||||
| 
 | ||||
|     let mut affiliated_keywords = affiliated_keywords.into_iter(); | ||||
| 
 | ||||
| @ -277,8 +276,7 @@ fn _detect_element<'b, 'g, 'r, 's>( | ||||
|     input: OrgSource<'s>, | ||||
|     can_be_paragraph: bool, | ||||
| ) -> Res<OrgSource<'s>, ()> { | ||||
|     let (post_affiliated_keywords_input, affiliated_keywords) = | ||||
|         affiliated_keywords(context, input)?; | ||||
|     let (post_affiliated_keywords_input, affiliated_keywords) = affiliated_keywords(input)?; | ||||
| 
 | ||||
|     let mut affiliated_keywords = affiliated_keywords.into_iter(); | ||||
| 
 | ||||
|  | ||||
| @ -84,7 +84,10 @@ fn in_buffer_settings_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSou | ||||
|     ))(input) | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug"))] | ||||
| #[cfg_attr(
 | ||||
|     feature = "tracing", | ||||
|     tracing::instrument(level = "debug", skip(original_settings)) | ||||
| )] | ||||
| pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>( | ||||
|     keywords: Vec<Keyword<'sf>>, | ||||
|     original_settings: &'g GlobalSettings<'g, 's>, | ||||
|  | ||||
| @ -21,7 +21,8 @@ use super::org_source::OrgSource; | ||||
| use super::util::get_consumed; | ||||
| use super::util::maybe_consume_trailing_whitespace_if_not_exiting; | ||||
| use super::util::org_line_ending; | ||||
| use crate::context::bind_context; | ||||
| use crate::context::constants::ORG_ELEMENT_AFFILIATED_KEYWORDS; | ||||
| use crate::context::constants::ORG_ELEMENT_DUAL_KEYWORDS; | ||||
| use crate::context::RefContext; | ||||
| use crate::error::CustomError; | ||||
| use crate::error::Res; | ||||
| @ -98,11 +99,8 @@ where | ||||
| } | ||||
| 
 | ||||
| #[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>> { | ||||
|     filtered_keyword(bind_context!(affiliated_key, context))(input) | ||||
| pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> { | ||||
|     filtered_keyword(affiliated_key)(input) | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(
 | ||||
| @ -137,24 +135,18 @@ fn regular_keyword_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| fn affiliated_key<'b, 'g, 'r, 's>( | ||||
|     context: RefContext<'b, 'g, 'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     element!(dual_affiliated_key, context, input); | ||||
|     element!(plain_affiliated_key, context, input); | ||||
| fn affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     element!(dual_affiliated_key, input); | ||||
|     element!(plain_affiliated_key, input); | ||||
|     element!(export_keyword, input); | ||||
|     Err(nom::Err::Error(CustomError::Static("No affiliated key."))) | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| fn plain_affiliated_key<'b, 'g, 'r, 's>( | ||||
|     context: RefContext<'b, 'g, 'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     for keyword in context.get_global_settings().element_affiliated_keywords { | ||||
| fn plain_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     for keyword in ORG_ELEMENT_AFFILIATED_KEYWORDS { | ||||
|         let result = map( | ||||
|             tuple((tag_no_case::<_, _, CustomError>(*keyword), peek(tag(":")))), | ||||
|             tuple((tag_no_case::<_, _, CustomError>(keyword), peek(tag(":")))), | ||||
|             |(key, _)| key, | ||||
|         )(input); | ||||
|         if let Ok((remaining, ent)) = result { | ||||
| @ -166,13 +158,10 @@ fn plain_affiliated_key<'b, 'g, 'r, 's>( | ||||
| } | ||||
| 
 | ||||
| #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] | ||||
| fn dual_affiliated_key<'b, 'g, 'r, 's>( | ||||
|     context: RefContext<'b, 'g, 'r, 's>, | ||||
|     input: OrgSource<'s>, | ||||
| ) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     for keyword in context.get_global_settings().element_dual_keywords { | ||||
| fn dual_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> { | ||||
|     for keyword in ORG_ELEMENT_DUAL_KEYWORDS { | ||||
|         let result = recognize(tuple(( | ||||
|             tag_no_case::<_, _, CustomError>(*keyword), | ||||
|             tag_no_case::<_, _, CustomError>(keyword), | ||||
|             tag("["), | ||||
|             optval, | ||||
|             tag("]"), | ||||
| @ -232,19 +221,12 @@ mod tests { | ||||
|     use test::Bencher; | ||||
| 
 | ||||
|     use super::*; | ||||
|     use crate::context::Context; | ||||
|     use crate::context::ContextElement; | ||||
|     use crate::context::GlobalSettings; | ||||
|     use crate::context::List; | ||||
|     use crate::parser::OrgSource; | ||||
| 
 | ||||
|     #[bench] | ||||
|     fn bench_affiliated_keyword(b: &mut Bencher) { | ||||
|         let input = OrgSource::new("#+CAPTION[*foo*]: bar *baz*"); | ||||
|         let global_settings = GlobalSettings::default(); | ||||
|         let initial_context = ContextElement::document_context(); | ||||
|         let initial_context = Context::new(&global_settings, List::new(&initial_context)); | ||||
| 
 | ||||
|         b.iter(|| assert!(affiliated_keyword(&initial_context, input).is_ok())); | ||||
|         b.iter(|| assert!(affiliated_keyword(input).is_ok())); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Alexander
						Tom Alexander