From 125b753abce6bac3156196ecee39a9b56911f1c3 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 17 Oct 2025 17:55:53 -0500 Subject: [PATCH 1/2] restore warnings in postprocessor --- crates/qmd-syntax-helper/src/main_old.rs | 201 ------------------ .../quarto-markdown-pandoc/src/readers/qmd.rs | 27 ++- .../tests/test_warnings.rs | 68 ++++++ 3 files changed, 90 insertions(+), 206 deletions(-) delete mode 100644 crates/qmd-syntax-helper/src/main_old.rs create mode 100644 crates/quarto-markdown-pandoc/tests/test_warnings.rs diff --git a/crates/qmd-syntax-helper/src/main_old.rs b/crates/qmd-syntax-helper/src/main_old.rs deleted file mode 100644 index b0b59f2..0000000 --- a/crates/qmd-syntax-helper/src/main_old.rs +++ /dev/null @@ -1,201 +0,0 @@ -use anyhow::Result; -use clap::{Parser, Subcommand}; -use std::path::PathBuf; - -mod conversions; -mod diagnostics; -mod problem; -mod utils; - -use conversions::definition_lists::DefinitionListConverter; -use conversions::div_whitespace::DivWhitespaceConverter; -use conversions::grid_tables::GridTableConverter; -use diagnostics::syntax_check::SyntaxChecker; -use utils::glob_expand::expand_globs; - -#[derive(Parser)] -#[command(name = "qmd-syntax-helper")] -#[command(about = "Helper tool for converting and fixing Quarto Markdown syntax")] -#[command(version)] -struct Cli { - #[command(subcommand)] - command: Commands, -} - -#[derive(Subcommand)] -enum Commands { - /// Convert grid tables to list-table format - UngridTables { - /// Input files (can be multiple files or glob patterns like "docs/**/*.qmd") - #[arg(required = true)] - files: Vec, - - /// Edit files in place - #[arg(short, long)] - in_place: bool, - - /// Check mode: show what would be changed without modifying files - #[arg(short, long)] - check: bool, - - /// Show verbose output - #[arg(short, long)] - verbose: bool, - }, - - /// Convert definition lists to div-based format - UndefLists { - /// Input files (can be multiple files or glob patterns like "docs/**/*.qmd") - #[arg(required = true)] - files: Vec, - - /// Edit files in place - #[arg(short, long)] - in_place: bool, - - /// Check mode: show what would be changed without modifying files - #[arg(short, long)] - check: bool, - - /// Show verbose output - #[arg(short, long)] - verbose: bool, - }, - - /// Fix div fences missing whitespace (:::{ -> ::: {) - FixDivWhitespace { - /// Input files (can be multiple files or glob patterns like "docs/**/*.qmd") - #[arg(required = true)] - files: Vec, - - /// Edit files in place - #[arg(short, long)] - in_place: bool, - - /// Check mode: show what would be changed without modifying files - #[arg(short, long)] - check: bool, - - /// Show verbose output - #[arg(short, long)] - verbose: bool, - }, - - /// Check syntax of files and report errors - Check { - /// Input files (can be multiple files or glob patterns like "docs/**/*.qmd") - #[arg(required = true)] - files: Vec, - - /// Show verbose output (each file as processed) - #[arg(short, long)] - verbose: bool, - - /// Output results as JSONL - #[arg(long)] - json: bool, - - /// Save detailed results to file - #[arg(short, long)] - output: Option, - }, -} - -fn main() -> Result<()> { - let cli = Cli::parse(); - - match cli.command { - Commands::UngridTables { - files, - in_place, - check, - verbose, - } => { - let converter = GridTableConverter::new()?; - let file_paths = expand_globs(&files)?; - - for file_path in file_paths { - if verbose { - println!("Processing: {}", file_path.display()); - } - - converter.process_file(&file_path, in_place, check, verbose)?; - } - - Ok(()) - } - Commands::UndefLists { - files, - in_place, - check, - verbose, - } => { - let converter = DefinitionListConverter::new()?; - let file_paths = expand_globs(&files)?; - - for file_path in file_paths { - if verbose { - println!("Processing: {}", file_path.display()); - } - - converter.process_file(&file_path, in_place, check, verbose)?; - } - - Ok(()) - } - Commands::FixDivWhitespace { - files, - in_place, - check, - verbose, - } => { - let converter = DivWhitespaceConverter::new()?; - let file_paths = expand_globs(&files)?; - - for file_path in file_paths { - if verbose { - println!("Processing: {}", file_path.display()); - } - - converter.process_file(&file_path, in_place, check, verbose)?; - } - - Ok(()) - } - Commands::Check { - files, - verbose, - json, - output, - } => { - let mut checker = SyntaxChecker::new(); - let file_paths = expand_globs(&files)?; - - for file_path in file_paths { - checker.check_file(&file_path, verbose)?; - } - - // Print summary if not JSON mode - if !json { - checker.print_summary(); - } - - // Save to output file if specified - if let Some(output_path) = output { - checker.export_jsonl(&output_path)?; - if !json { - println!("\nDetailed results written to: {}", output_path.display()); - } - } - - // Print JSON to stdout if requested - if json { - for result in &checker.results { - println!("{}", serde_json::to_string(result)?); - } - } - - Ok(()) - } - } -} diff --git a/crates/quarto-markdown-pandoc/src/readers/qmd.rs b/crates/quarto-markdown-pandoc/src/readers/qmd.rs index c799dad..73e7950 100644 --- a/crates/quarto-markdown-pandoc/src/readers/qmd.rs +++ b/crates/quarto-markdown-pandoc/src/readers/qmd.rs @@ -16,7 +16,7 @@ use crate::pandoc::{self, Block, Meta}; use crate::pandoc::{MetaValue, rawblock_to_meta}; use crate::readers::qmd_error_messages::{produce_error_message, produce_error_message_json}; use crate::traversals; -use crate::utils::error_collector::{JsonErrorCollector, TextErrorCollector}; +use crate::utils::error_collector::{ErrorCollector, JsonErrorCollector, TextErrorCollector}; use std::io::Write; use tree_sitter::LogType; use tree_sitter_qmd::MarkdownParser; @@ -140,26 +140,43 @@ where let context = ASTContext::with_filename(filename.to_string()); // Create appropriate error collector based on whether JSON errors are requested + // and collect warnings after conversion let mut result = if error_formatter.is_some() { // JSON error format requested let mut error_collector = JsonErrorCollector::new(); - pandoc::treesitter_to_pandoc( + let pandoc_result = pandoc::treesitter_to_pandoc( &mut output_stream, &tree, &input_bytes, &context, &mut error_collector, - )? + )?; + + // Output warnings to stderr as JSON + let warnings = error_collector.messages(); + for warning in warnings { + eprintln!("{}", warning); + } + + pandoc_result } else { // Text error format (default) let mut error_collector = TextErrorCollector::new(); - pandoc::treesitter_to_pandoc( + let pandoc_result = pandoc::treesitter_to_pandoc( &mut output_stream, &tree, &input_bytes, &context, &mut error_collector, - )? + )?; + + // Output warnings to stderr as formatted text + let warnings = error_collector.messages(); + for warning in warnings { + eprintln!("{}", warning); + } + + pandoc_result }; let mut meta_from_parses = Meta::default(); diff --git a/crates/quarto-markdown-pandoc/tests/test_warnings.rs b/crates/quarto-markdown-pandoc/tests/test_warnings.rs new file mode 100644 index 0000000..58f7330 --- /dev/null +++ b/crates/quarto-markdown-pandoc/tests/test_warnings.rs @@ -0,0 +1,68 @@ +use quarto_markdown_pandoc::readers; +use quarto_markdown_pandoc::utils; + +#[test] +fn test_caption_without_table_warning() { + // Create input with a caption after a div (not a table) + // This should parse successfully but emit a warning + let input = r#"::: {.my-div} +Some content +::: + +: This caption has no table +"#; + + // Parse the document + let result = readers::qmd::read( + input.as_bytes(), + false, + "test.md", + &mut std::io::sink(), + None::< + fn(&[u8], &utils::tree_sitter_log_observer::TreeSitterLogObserver, &str) -> Vec, + >, + ); + + // Parsing should succeed (warnings are not errors) + assert!(result.is_ok(), "Document should parse successfully despite warning"); + + // TODO: Once the fix is implemented, we need to verify that the warning + // "Caption found without a preceding table" was actually output. + // For now, this test just verifies that parsing succeeds. + // After the fix, we'll need to capture stderr or modify the API + // to return warnings alongside the successful parse result. +} + +#[test] +fn test_caption_with_table_no_warning() { + // Create input with a proper table caption + // This should parse successfully with no warnings + let input = r#"| A | B | +|---|---| +| 1 | 2 | + +: Table caption +"#; + + // Parse the document + let result = readers::qmd::read( + input.as_bytes(), + false, + "test.md", + &mut std::io::sink(), + None::< + fn(&[u8], &utils::tree_sitter_log_observer::TreeSitterLogObserver, &str) -> Vec, + >, + ); + + // Parsing should succeed and no warnings should be emitted + assert!(result.is_ok(), "Document with valid table caption should parse successfully"); + + let (pandoc, _context) = result.unwrap(); + + // Verify we have a table in the output + assert!( + pandoc.blocks.iter().any(|b| matches!(b, quarto_markdown_pandoc::pandoc::Block::Table(_))), + "Should have a table in the output" + ); +} From 9aad301630c47f9651381b9bba2273406599335f Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 17 Oct 2025 18:02:01 -0500 Subject: [PATCH 2/2] more json errors --- crates/quarto-markdown-pandoc/src/main.rs | 13 ++++++++++-- .../tests/test_json_errors.rs | 21 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/crates/quarto-markdown-pandoc/src/main.rs b/crates/quarto-markdown-pandoc/src/main.rs index d46622c..9bf4f60 100644 --- a/crates/quarto-markdown-pandoc/src/main.rs +++ b/crates/quarto-markdown-pandoc/src/main.rs @@ -89,8 +89,17 @@ fn main() { } if !input.ends_with("\n") { - eprintln!("(Warning) Adding missing newline to end of input."); - // + if args.json_errors { + // Output as JSON to stderr + let warning_json = serde_json::json!({ + "title": "Warning", + "message": "Adding missing newline to end of input" + }); + eprintln!("{}", warning_json); + } else { + // Output as plain text to stderr + eprintln!("(Warning) Adding missing newline to end of input."); + } input.push('\n'); // ensure the input ends with a newline } diff --git a/crates/quarto-markdown-pandoc/tests/test_json_errors.rs b/crates/quarto-markdown-pandoc/tests/test_json_errors.rs index 29eaffd..80fd0d8 100644 --- a/crates/quarto-markdown-pandoc/tests/test_json_errors.rs +++ b/crates/quarto-markdown-pandoc/tests/test_json_errors.rs @@ -158,3 +158,24 @@ fn test_label_range_note_type() { "Should find at least one label-range note in the error" ); } + +#[test] +fn test_missing_newline_warning_json_format() { + // This test verifies that the missing newline warning is formatted as JSON + // when --json-errors is used. Currently this test will fail because the + // warning is always output as plain text. + + // Create input without trailing newline + let input = "# Hello World"; + + // We can't easily test the binary's stderr output from here, but we can + // document the expected behavior: when --json-errors is used, the warning + // should be output as: + // {"title":"Warning","message":"Adding missing newline to end of input"} + // + // Currently it outputs: + // (Warning) Adding missing newline to end of input. + + // This test just documents the issue. The actual fix will be in main.rs + // where the warning is emitted. +}