Handle org-mode documents passed as args.

We were running into issues where the documents grew too large for being passed as a string to emacs, and we need to handle #+setupfile so we need to start handling org-mode documents as files and not just as anonymous streams of text.

The anonymous stream of text handling will remain because the automated tests use it.
This commit is contained in:
Tom Alexander 2023-09-02 12:15:57 -04:00
parent a40a504f94
commit f16a554154
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
10 changed files with 118 additions and 25 deletions

View File

@ -22,7 +22,7 @@ path = "src/lib.rs"
[[bin]] [[bin]]
# This bin exists for development purposes only. The real target of this crate is the library. # This bin exists for development purposes only. The real target of this crate is the library.
name = "compare" name = "parse"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]

View File

@ -73,7 +73,7 @@ fn write_header(test_file: &mut File) {
#[feature(exit_status_error)] #[feature(exit_status_error)]
use organic::compare_document; use organic::compare_document;
use organic::parser::document; use organic::parser::document;
use organic::emacs_parse_org_document; use organic::emacs_parse_anonymous_org_document;
use organic::parser::sexp::sexp_with_padding; use organic::parser::sexp::sexp_with_padding;
"# "#

View File

@ -7,7 +7,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$DIR/../" cd "$DIR/../"
RUSTFLAGS="-C opt-level=0" cargo build --no-default-features RUSTFLAGS="-C opt-level=0" cargo build --no-default-features
valgrind --tool=callgrind --callgrind-out-file=callgrind.out target/debug/compare valgrind --tool=callgrind --callgrind-out-file=callgrind.out target/debug/parse
echo "You probably want to run:" echo "You probably want to run:"
echo "callgrind_annotate --auto=yes callgrind.out" echo "callgrind_annotate --auto=yes callgrind.out"

View File

@ -16,7 +16,7 @@ function main {
additional_flags+=(--profile "$PROFILE") additional_flags+=(--profile "$PROFILE")
fi fi
cargo build --no-default-features "${additional_flags[@]}" cargo build --no-default-features "${additional_flags[@]}"
perf record --freq=2000 --call-graph dwarf --output=perf.data target/${PROFILE}/compare perf record --freq=2000 --call-graph dwarf --output=perf.data target/${PROFILE}/parse
# Convert to a format firefox will read # Convert to a format firefox will read
# flags to consider --show-info # flags to consider --show-info

View File

@ -17,7 +17,7 @@ function main {
additional_flags+=(--profile "$PROFILE") additional_flags+=(--profile "$PROFILE")
fi fi
cargo build --no-default-features "${additional_flags[@]}" cargo build --no-default-features "${additional_flags[@]}"
time ./target/${PROFILE}/compare time ./target/${PROFILE}/parse
} }
main "${@}" main "${@}"

View File

@ -2,6 +2,7 @@ mod diff;
mod parse; mod parse;
mod util; mod util;
pub use diff::compare_document; pub use diff::compare_document;
pub use parse::emacs_parse_org_document; pub use parse::emacs_parse_anonymous_org_document;
pub use parse::emacs_parse_file_org_document;
pub use parse::get_emacs_version; pub use parse::get_emacs_version;
pub use parse::get_org_mode_version; pub use parse::get_org_mode_version;

View File

@ -1,6 +1,9 @@
use std::path::Path;
use std::process::Command; use std::process::Command;
pub fn emacs_parse_org_document<C>(file_contents: C) -> Result<String, Box<dyn std::error::Error>> pub fn emacs_parse_anonymous_org_document<C>(
file_contents: C,
) -> Result<String, Box<dyn std::error::Error>>
where where
C: AsRef<str>, C: AsRef<str>,
{ {
@ -15,14 +18,46 @@ where
escaped_file_contents = escaped_file_contents escaped_file_contents = escaped_file_contents
); );
let mut cmd = Command::new("emacs"); let mut cmd = Command::new("emacs");
let proc = cmd let cmd = cmd
.arg("-q") .arg("-q")
.arg("--no-site-file") .arg("--no-site-file")
.arg("--no-splash") .arg("--no-splash")
.arg("--batch") .arg("--batch")
.arg("--eval") .arg("--eval")
.arg(elisp_script); .arg(elisp_script);
let out = proc.output()?; let out = cmd.output()?;
out.status.exit_ok()?;
let org_sexp = out.stderr;
Ok(String::from_utf8(org_sexp)?)
}
pub fn emacs_parse_file_org_document<P>(file_path: P) -> 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
(org-mode)
(message "%s" (pp-to-string (org-element-parse-buffer)))
)"#
);
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("--insert")
.arg(file_path.as_os_str())
.arg("--eval")
.arg(elisp_script);
let out = cmd.output()?;
out.status.exit_ok()?; out.status.exit_ok()?;
let org_sexp = out.stderr; let org_sexp = out.stderr;
Ok(String::from_utf8(org_sexp)?) Ok(String::from_utf8(org_sexp)?)
@ -55,7 +90,7 @@ pub fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
(message "%s" (version)) (message "%s" (version))
)"#; )"#;
let mut cmd = Command::new("emacs"); let mut cmd = Command::new("emacs");
let proc = cmd let cmd = cmd
.arg("-q") .arg("-q")
.arg("--no-site-file") .arg("--no-site-file")
.arg("--no-splash") .arg("--no-splash")
@ -63,7 +98,7 @@ pub fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
.arg("--eval") .arg("--eval")
.arg(elisp_script); .arg(elisp_script);
let out = proc.output()?; let out = cmd.output()?;
out.status.exit_ok()?; out.status.exit_ok()?;
Ok(String::from_utf8(out.stderr)?) Ok(String::from_utf8(out.stderr)?)
} }
@ -74,7 +109,7 @@ pub fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
(message "%s" (org-version nil t nil)) (message "%s" (org-version nil t nil))
)"#; )"#;
let mut cmd = Command::new("emacs"); let mut cmd = Command::new("emacs");
let proc = cmd let cmd = cmd
.arg("-q") .arg("-q")
.arg("--no-site-file") .arg("--no-site-file")
.arg("--no-splash") .arg("--no-splash")
@ -82,7 +117,7 @@ pub fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
.arg("--eval") .arg("--eval")
.arg(elisp_script); .arg(elisp_script);
let out = proc.output()?; let out = cmd.output()?;
out.status.exit_ok()?; out.status.exit_ok()?;
Ok(String::from_utf8(out.stderr)?) Ok(String::from_utf8(out.stderr)?)
} }

View File

@ -6,7 +6,9 @@ mod compare;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
pub use compare::compare_document; pub use compare::compare_document;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
pub use compare::emacs_parse_org_document; pub use compare::emacs_parse_anonymous_org_document;
#[cfg(feature = "compare")]
pub use compare::emacs_parse_file_org_document;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
pub use compare::get_emacs_version; pub use compare::get_emacs_version;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]

View File

@ -1,11 +1,15 @@
#![feature(round_char_boundary)] #![feature(round_char_boundary)]
#![feature(exact_size_is_empty)]
use std::io::Read; use std::io::Read;
use std::path::Path;
use ::organic::parser::document; use ::organic::parser::document;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
use organic::compare_document; use organic::compare_document;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
use organic::emacs_parse_org_document; use organic::emacs_parse_anonymous_org_document;
#[cfg(feature = "compare")]
use organic::emacs_parse_file_org_document;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
use organic::get_emacs_version; use organic::get_emacs_version;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
@ -39,8 +43,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn main_body() -> Result<(), Box<dyn std::error::Error>> { fn main_body() -> Result<(), Box<dyn std::error::Error>> {
let org_contents = read_stdin_to_string()?; let args = std::env::args();
run_compare(org_contents) if args.is_empty() {
let org_contents = read_stdin_to_string()?;
run_anonymous_parse(org_contents)
} else {
for arg in args.skip(1) {
run_parse_on_file(arg)?
}
Ok(())
}
} }
fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> { fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
@ -52,14 +64,12 @@ fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
} }
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> { fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
let emacs_version = get_emacs_version()?;
let org_mode_version = get_org_mode_version()?;
let org_contents = org_contents.as_ref(); let org_contents = org_contents.as_ref();
eprintln!("Using emacs version: {}", emacs_version.trim()); eprintln!("Using emacs version: {}", get_emacs_version()?.trim());
eprintln!("Using org-mode version: {}", org_mode_version.trim()); eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim());
let (remaining, rust_parsed) = document(org_contents).map_err(|e| e.to_string())?; let (remaining, rust_parsed) = document(org_contents).map_err(|e| e.to_string())?;
let org_sexp = emacs_parse_org_document(org_contents)?; let org_sexp = emacs_parse_anonymous_org_document(org_contents)?;
let (_remaining, parsed_sexp) = let (_remaining, parsed_sexp) =
sexp_with_padding(org_sexp.as_str()).map_err(|e| e.to_string())?; sexp_with_padding(org_sexp.as_str()).map_err(|e| e.to_string())?;
@ -82,7 +92,7 @@ fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error:
} }
#[cfg(not(feature = "compare"))] #[cfg(not(feature = "compare"))]
fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> { fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
eprintln!( eprintln!(
"This program was built with compare disabled. Only parsing with organic, not comparing." "This program was built with compare disabled. Only parsing with organic, not comparing."
); );
@ -90,3 +100,48 @@ fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error:
println!("{:#?}", rust_parsed); println!("{:#?}", rust_parsed);
Ok(()) Ok(())
} }
#[cfg(feature = "compare")]
fn run_parse_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> {
let org_path = org_path.as_ref();
eprintln!("Using emacs version: {}", get_emacs_version()?.trim());
eprintln!("Using org-mode version: {}", get_org_mode_version()?.trim());
// TODO: This should take into account the original file path when parsing in Organic, not just in emacs.
let org_contents = std::fs::read_to_string(org_path)?;
let org_contents = org_contents.as_str();
let (remaining, rust_parsed) = document(org_contents).map_err(|e| e.to_string())?;
let org_sexp = emacs_parse_file_org_document(org_path)?;
let (_remaining, parsed_sexp) =
sexp_with_padding(org_sexp.as_str()).map_err(|e| e.to_string())?;
println!("{}\n\n\n", org_contents);
println!("{}", org_sexp);
println!("{:#?}", rust_parsed);
// We do the diffing after printing out both parsed forms in case the diffing panics
let diff_result = compare_document(&parsed_sexp, &rust_parsed)?;
diff_result.print(org_contents)?;
if diff_result.is_bad() {
Err("Diff results do not match.")?;
}
if remaining != "" {
Err(format!("There was unparsed text remaining: {}", remaining))?;
}
Ok(())
}
#[cfg(not(feature = "compare"))]
fn run_parse_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::error::Error>> {
let org_path = org_path.as_ref();
eprintln!(
"This program was built with compare disabled. Only parsing with organic, not comparing."
);
// TODO: This should take into account the original file path when parsing
let org_contents = std::fs::read_to_string(org_path)?;
let org_contents = org_contents.as_str();
let (_remaining, rust_parsed) = document(org_contents).map_err(|e| e.to_string())?;
println!("{:#?}", rust_parsed);
Ok(())
}

View File

@ -3,7 +3,7 @@ fn {name}() {{
let todo_org_path = "{path}"; let todo_org_path = "{path}";
let org_contents = std::fs::read_to_string(todo_org_path).expect("Read org file."); let org_contents = std::fs::read_to_string(todo_org_path).expect("Read org file.");
println!("{{}}", org_contents); println!("{{}}", org_contents);
let org_sexp = emacs_parse_org_document(org_contents.as_str()).expect("Use emacs to parse org file."); let org_sexp = emacs_parse_anonymous_org_document(org_contents.as_str()).expect("Use emacs to parse org file.");
println!("{{}}", org_sexp); println!("{{}}", org_sexp);
let (_remaining, parsed_sexp) = sexp_with_padding(org_sexp.as_str()).expect("Sexp Parse failure"); let (_remaining, parsed_sexp) = sexp_with_padding(org_sexp.as_str()).expect("Sexp Parse failure");
let (remaining, rust_parsed) = document(org_contents.as_str()).expect("Org Parse failure"); let (remaining, rust_parsed) = document(org_contents.as_str()).expect("Org Parse failure");