Move the parsing of the elisp to the util module.
This commit is contained in:
		
							parent
							
								
									c2e921c2dc
								
							
						
					
					
						commit
						c717541099
					
				| @ -2,13 +2,13 @@ use std::path::Path; | ||||
| 
 | ||||
| use crate::compare::diff::compare_document; | ||||
| use crate::compare::diff::DiffResult; | ||||
| use crate::compare::parse::emacs_parse_anonymous_org_document; | ||||
| use crate::compare::parse::emacs_parse_file_org_document; | ||||
| use crate::compare::sexp::sexp; | ||||
| use crate::context::GlobalSettings; | ||||
| use crate::context::LocalFileAccessInterface; | ||||
| use crate::parser::parse_file_with_settings; | ||||
| use crate::parser::parse_with_settings; | ||||
| use crate::util::emacs_parse_anonymous_org_document; | ||||
| use crate::util::emacs_parse_file_org_document; | ||||
| use crate::util::print_versions; | ||||
| 
 | ||||
| pub async fn run_anonymous_compare<P: AsRef<str>>( | ||||
|  | ||||
| @ -4,7 +4,6 @@ mod compare_field; | ||||
| mod diff; | ||||
| mod elisp_fact; | ||||
| mod macros; | ||||
| mod parse; | ||||
| mod sexp; | ||||
| mod util; | ||||
| pub use compare::run_anonymous_compare; | ||||
|  | ||||
| @ -1,145 +0,0 @@ | ||||
| use std::path::Path; | ||||
| 
 | ||||
| use tokio::process::Command; | ||||
| 
 | ||||
| use crate::context::HeadlineLevelFilter; | ||||
| use crate::settings::GlobalSettings; | ||||
| 
 | ||||
| /// Generate elisp to configure org-mode parsing settings
 | ||||
| ///
 | ||||
| /// Currently only org-list-allow-alphabetical is supported.
 | ||||
| fn global_settings_elisp(global_settings: &GlobalSettings) -> String { | ||||
|     // This string concatenation is wildly inefficient but its only called in tests 🤷.
 | ||||
|     let mut ret = "".to_owned(); | ||||
|     if global_settings.list_allow_alphabetical { | ||||
|         ret += "(setq org-list-allow-alphabetical t)\n" | ||||
|     } | ||||
|     if global_settings.tab_width != crate::settings::DEFAULT_TAB_WIDTH { | ||||
|         ret += format!("(setq-default tab-width {})", global_settings.tab_width).as_str(); | ||||
|     } | ||||
|     if global_settings.odd_levels_only != HeadlineLevelFilter::default() { | ||||
|         ret += match global_settings.odd_levels_only { | ||||
|             HeadlineLevelFilter::Odd => "(setq org-odd-levels-only t)\n", | ||||
|             HeadlineLevelFilter::OddEven => "(setq org-odd-levels-only nil)\n", | ||||
|         }; | ||||
|     } | ||||
|     ret | ||||
| } | ||||
| 
 | ||||
| pub(crate) async fn emacs_parse_anonymous_org_document<'g, 's, C>( | ||||
|     file_contents: C, | ||||
|     global_settings: &GlobalSettings<'g, 's>, | ||||
| ) -> Result<String, Box<dyn std::error::Error>> | ||||
| where | ||||
|     C: AsRef<str>, | ||||
| { | ||||
|     let escaped_file_contents = escape_elisp_string(file_contents); | ||||
|     let elisp_script = format!( | ||||
|         r#"(progn
 | ||||
|      (erase-buffer) | ||||
|      (require 'org) | ||||
|      (defun org-table-align () t) | ||||
|      (insert "{escaped_file_contents}") | ||||
|      {global_settings} | ||||
|      (org-mode) | ||||
|      (message "%s" (pp-to-string (org-element-parse-buffer))) | ||||
| )"#,
 | ||||
|         escaped_file_contents = escaped_file_contents, | ||||
|         global_settings = global_settings_elisp(global_settings) | ||||
|     ); | ||||
|     let mut cmd = Command::new("emacs"); | ||||
|     let cmd = cmd | ||||
|         .arg("-q") | ||||
|         .arg("--no-site-file") | ||||
|         .arg("--no-splash") | ||||
|         .arg("--batch") | ||||
|         .arg("--eval") | ||||
|         .arg(elisp_script); | ||||
|     let out = cmd.output().await?; | ||||
|     let status = out.status.exit_ok(); | ||||
|     if status.is_err() { | ||||
|         eprintln!( | ||||
|             "Emacs errored out: {}\n{}", | ||||
|             String::from_utf8(out.stdout)?, | ||||
|             String::from_utf8(out.stderr)? | ||||
|         ); | ||||
|         status?; | ||||
|         unreachable!(); | ||||
|     } | ||||
|     let org_sexp = out.stderr; | ||||
|     Ok(String::from_utf8(org_sexp)?) | ||||
| } | ||||
| 
 | ||||
| pub(crate) async fn emacs_parse_file_org_document<'g, 's, P>( | ||||
|     file_path: P, | ||||
|     global_settings: &GlobalSettings<'g, 's>, | ||||
| ) -> Result<String, Box<dyn std::error::Error>> | ||||
| where | ||||
|     P: AsRef<Path>, | ||||
| { | ||||
|     let file_path = file_path.as_ref().canonicalize()?; | ||||
|     let containing_directory = file_path.parent().ok_or(format!( | ||||
|         "Failed to get containing directory for path {}", | ||||
|         file_path.display() | ||||
|     ))?; | ||||
|     let elisp_script = format!( | ||||
|         r#"(progn
 | ||||
|      (require 'org) | ||||
|      (defun org-table-align () t) | ||||
|      (setq vc-handled-backends nil) | ||||
|      {global_settings} | ||||
|      (find-file-read-only "{file_path}") | ||||
|      (org-mode) | ||||
|      (message "%s" (pp-to-string (org-element-parse-buffer))) | ||||
| )"#,
 | ||||
|         global_settings = global_settings_elisp(global_settings), | ||||
|         file_path = file_path | ||||
|             .as_os_str() | ||||
|             .to_str() | ||||
|             .expect("File name should be valid utf-8.") | ||||
|     ); | ||||
|     let mut cmd = Command::new("emacs"); | ||||
|     let cmd = cmd | ||||
|         .current_dir(containing_directory) | ||||
|         .arg("-q") | ||||
|         .arg("--no-site-file") | ||||
|         .arg("--no-splash") | ||||
|         .arg("--batch") | ||||
|         .arg("--eval") | ||||
|         .arg(elisp_script); | ||||
|     let out = cmd.output().await?; | ||||
|     let status = out.status.exit_ok(); | ||||
|     if status.is_err() { | ||||
|         eprintln!( | ||||
|             "Emacs errored out: {}\n{}", | ||||
|             String::from_utf8(out.stdout)?, | ||||
|             String::from_utf8(out.stderr)? | ||||
|         ); | ||||
|         status?; | ||||
|         unreachable!(); | ||||
|     } | ||||
|     let org_sexp = out.stderr; | ||||
|     Ok(String::from_utf8(org_sexp)?) | ||||
| } | ||||
| 
 | ||||
| fn escape_elisp_string<C>(file_contents: C) -> String | ||||
| where | ||||
|     C: AsRef<str>, | ||||
| { | ||||
|     let source = file_contents.as_ref(); | ||||
|     let source_len = source.len(); | ||||
|     // We allocate a string 10% larger than the source to account for escape characters. Without this, we would have more allocations during processing.
 | ||||
|     let mut output = String::with_capacity(source_len + (source_len / 10)); | ||||
|     for c in source.chars() { | ||||
|         match c { | ||||
|             '"' | '\\' => { | ||||
|                 output.push('\\'); | ||||
|                 output.push(c); | ||||
|             } | ||||
|             _ => { | ||||
|                 output.push(c); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     output | ||||
| } | ||||
							
								
								
									
										144
									
								
								src/util/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								src/util/mod.rs
									
									
									
									
									
								
							| @ -1,5 +1,10 @@ | ||||
| use std::path::Path; | ||||
| 
 | ||||
| use tokio::process::Command; | ||||
| 
 | ||||
| use crate::settings::GlobalSettings; | ||||
| use crate::settings::HeadlineLevelFilter; | ||||
| 
 | ||||
| pub async fn print_versions() -> Result<(), Box<dyn std::error::Error>> { | ||||
|     eprintln!("Using emacs version: {}", get_emacs_version().await?.trim()); | ||||
|     eprintln!( | ||||
| @ -45,3 +50,142 @@ pub(crate) async fn get_org_mode_version() -> Result<String, Box<dyn std::error: | ||||
|     out.status.exit_ok()?; | ||||
|     Ok(String::from_utf8(out.stderr)?) | ||||
| } | ||||
| 
 | ||||
| pub(crate) async fn emacs_parse_anonymous_org_document<'g, 's, C>( | ||||
|     file_contents: C, | ||||
|     global_settings: &GlobalSettings<'g, 's>, | ||||
| ) -> Result<String, Box<dyn std::error::Error>> | ||||
| where | ||||
|     C: AsRef<str>, | ||||
| { | ||||
|     let escaped_file_contents = escape_elisp_string(file_contents); | ||||
|     let elisp_script = format!( | ||||
|         r#"(progn
 | ||||
|      (erase-buffer) | ||||
|      (require 'org) | ||||
|      (defun org-table-align () t) | ||||
|      (insert "{escaped_file_contents}") | ||||
|      {global_settings} | ||||
|      (org-mode) | ||||
|      (message "%s" (pp-to-string (org-element-parse-buffer))) | ||||
| )"#,
 | ||||
|         escaped_file_contents = escaped_file_contents, | ||||
|         global_settings = global_settings_elisp(global_settings) | ||||
|     ); | ||||
|     let mut cmd = Command::new("emacs"); | ||||
|     let cmd = cmd | ||||
|         .arg("-q") | ||||
|         .arg("--no-site-file") | ||||
|         .arg("--no-splash") | ||||
|         .arg("--batch") | ||||
|         .arg("--eval") | ||||
|         .arg(elisp_script); | ||||
|     let out = cmd.output().await?; | ||||
|     let status = out.status.exit_ok(); | ||||
|     if status.is_err() { | ||||
|         eprintln!( | ||||
|             "Emacs errored out: {}\n{}", | ||||
|             String::from_utf8(out.stdout)?, | ||||
|             String::from_utf8(out.stderr)? | ||||
|         ); | ||||
|         status?; | ||||
|         unreachable!(); | ||||
|     } | ||||
|     let org_sexp = out.stderr; | ||||
|     Ok(String::from_utf8(org_sexp)?) | ||||
| } | ||||
| 
 | ||||
| pub(crate) async fn emacs_parse_file_org_document<'g, 's, P>( | ||||
|     file_path: P, | ||||
|     global_settings: &GlobalSettings<'g, 's>, | ||||
| ) -> Result<String, Box<dyn std::error::Error>> | ||||
| where | ||||
|     P: AsRef<Path>, | ||||
| { | ||||
|     let file_path = file_path.as_ref().canonicalize()?; | ||||
|     let containing_directory = file_path.parent().ok_or(format!( | ||||
|         "Failed to get containing directory for path {}", | ||||
|         file_path.display() | ||||
|     ))?; | ||||
|     let elisp_script = format!( | ||||
|         r#"(progn
 | ||||
|      (require 'org) | ||||
|      (defun org-table-align () t) | ||||
|      (setq vc-handled-backends nil) | ||||
|      {global_settings} | ||||
|      (find-file-read-only "{file_path}") | ||||
|      (org-mode) | ||||
|      (message "%s" (pp-to-string (org-element-parse-buffer))) | ||||
| )"#,
 | ||||
|         global_settings = global_settings_elisp(global_settings), | ||||
|         file_path = file_path | ||||
|             .as_os_str() | ||||
|             .to_str() | ||||
|             .expect("File name should be valid utf-8.") | ||||
|     ); | ||||
|     let mut cmd = Command::new("emacs"); | ||||
|     let cmd = cmd | ||||
|         .current_dir(containing_directory) | ||||
|         .arg("-q") | ||||
|         .arg("--no-site-file") | ||||
|         .arg("--no-splash") | ||||
|         .arg("--batch") | ||||
|         .arg("--eval") | ||||
|         .arg(elisp_script); | ||||
|     let out = cmd.output().await?; | ||||
|     let status = out.status.exit_ok(); | ||||
|     if status.is_err() { | ||||
|         eprintln!( | ||||
|             "Emacs errored out: {}\n{}", | ||||
|             String::from_utf8(out.stdout)?, | ||||
|             String::from_utf8(out.stderr)? | ||||
|         ); | ||||
|         status?; | ||||
|         unreachable!(); | ||||
|     } | ||||
|     let org_sexp = out.stderr; | ||||
|     Ok(String::from_utf8(org_sexp)?) | ||||
| } | ||||
| 
 | ||||
| fn escape_elisp_string<C>(file_contents: C) -> String | ||||
| where | ||||
|     C: AsRef<str>, | ||||
| { | ||||
|     let source = file_contents.as_ref(); | ||||
|     let source_len = source.len(); | ||||
|     // We allocate a string 10% larger than the source to account for escape characters. Without this, we would have more allocations during processing.
 | ||||
|     let mut output = String::with_capacity(source_len + (source_len / 10)); | ||||
|     for c in source.chars() { | ||||
|         match c { | ||||
|             '"' | '\\' => { | ||||
|                 output.push('\\'); | ||||
|                 output.push(c); | ||||
|             } | ||||
|             _ => { | ||||
|                 output.push(c); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     output | ||||
| } | ||||
| 
 | ||||
| /// Generate elisp to configure org-mode parsing settings
 | ||||
| ///
 | ||||
| /// Currently only org-list-allow-alphabetical is supported.
 | ||||
| fn global_settings_elisp(global_settings: &GlobalSettings) -> String { | ||||
|     // This string concatenation is wildly inefficient but its only called in tests 🤷.
 | ||||
|     let mut ret = "".to_owned(); | ||||
|     if global_settings.list_allow_alphabetical { | ||||
|         ret += "(setq org-list-allow-alphabetical t)\n" | ||||
|     } | ||||
|     if global_settings.tab_width != crate::settings::DEFAULT_TAB_WIDTH { | ||||
|         ret += format!("(setq-default tab-width {})", global_settings.tab_width).as_str(); | ||||
|     } | ||||
|     if global_settings.odd_levels_only != HeadlineLevelFilter::default() { | ||||
|         ret += match global_settings.odd_levels_only { | ||||
|             HeadlineLevelFilter::Odd => "(setq org-odd-levels-only t)\n", | ||||
|             HeadlineLevelFilter::OddEven => "(setq org-odd-levels-only nil)\n", | ||||
|         }; | ||||
|     } | ||||
|     ret | ||||
| } | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| use crate::context::GlobalSettings; | ||||
| use crate::parser::parse_with_settings; | ||||
| use crate::util::emacs_parse_anonymous_org_document; | ||||
| use crate::util::print_versions; | ||||
| 
 | ||||
| pub async fn wasm_run_anonymous_compare<P: AsRef<str>>( | ||||
| @ -20,7 +21,7 @@ pub async fn wasm_run_anonymous_compare_with_settings<'g, 's, P: AsRef<str>>( | ||||
|         print_versions().await?; | ||||
|     } | ||||
|     let rust_parsed = parse_with_settings(org_contents, global_settings)?; | ||||
|     // let org_sexp = emacs_parse_anonymous_org_document(org_contents, global_settings).await?;
 | ||||
|     let org_sexp = emacs_parse_anonymous_org_document(org_contents, global_settings).await?; | ||||
|     // let (_remaining, parsed_sexp) = sexp(org_sexp.as_str()).map_err(|e| e.to_string())?;
 | ||||
| 
 | ||||
|     if !silent { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Alexander
						Tom Alexander