eure_mark/
report.rs

1//! Error reporting for eure-mark using eure's ErrorReports system
2
3use eure::document::OriginMap;
4use eure::query::assets::TextFile;
5use eure::query_flow::{Db, QueryError};
6use eure::report::{ErrorReport, ErrorReports, Origin};
7use eure::tree::Cst;
8use eure_tree::tree::InputSpan;
9
10use crate::check::CheckResult;
11use crate::error::ReferenceError;
12
13/// Context for converting eure-mark errors to ErrorReports
14pub struct EumdReportContext<'a> {
15    /// File for the eumd document
16    pub file: TextFile,
17    /// CST for span resolution
18    pub cst: &'a Cst,
19    /// Origin map with precise origins
20    pub origins: &'a OriginMap,
21}
22
23/// Convert eure-mark check result to ErrorReports
24pub fn report_check_errors(result: &CheckResult, ctx: &EumdReportContext<'_>) -> ErrorReports {
25    result
26        .errors
27        .iter()
28        .map(|error| report_reference_error(error, ctx))
29        .collect()
30}
31
32fn report_reference_error(error: &ReferenceError, ctx: &EumdReportContext<'_>) -> ErrorReport {
33    let title = format!(
34        "Undefined !{}[{}] {}",
35        error.ref_type, error.key, error.location
36    );
37
38    // Try to get span from NodeId if available
39    let span = if let (Some(node_id), Some(offset), Some(len)) =
40        (error.node_id, error.offset, error.len)
41    {
42        // Get the span of the node (code block)
43        if let Some(node_span) = ctx.origins.get_value_span(node_id, ctx.cst) {
44            // For code blocks, we need to find where the actual content starts
45            // The node_span includes the opening ``` and language tag
46            // We'll calculate the content start by looking at the text
47            let content_start = get_code_block_content_start(ctx, node_span);
48
49            let start = content_start + offset;
50            let end = start + len;
51            InputSpan { start, end }
52        } else {
53            InputSpan::EMPTY
54        }
55    } else {
56        InputSpan::EMPTY
57    };
58
59    let origin = Origin::new(ctx.file.clone(), span);
60    ErrorReport::error(title, origin)
61}
62
63/// Get the byte offset where the code block content starts
64fn get_code_block_content_start(_ctx: &EumdReportContext<'_>, node_span: InputSpan) -> u32 {
65    // For code blocks, we need to skip past the opening ``` and language tag
66    // The Text type in eure stores the content without the delimiters,
67    // but the node span includes everything.
68    //
69    // For now, use the node span start which points to the content area.
70    // The span resolution in OriginMap should give us a reasonable position.
71    node_span.start
72}
73
74/// Format check errors to a string using annotate-snippets.
75///
76/// Returns `Err` with suspension if file content isn't loaded yet.
77pub fn format_check_errors(
78    db: &impl Db,
79    result: &CheckResult,
80    file: TextFile,
81    cst: &Cst,
82    origins: &OriginMap,
83    styled: bool,
84) -> Result<String, QueryError> {
85    let ctx = EumdReportContext { file, cst, origins };
86    let reports = report_check_errors(result, &ctx);
87    eure::report::format_error_reports(db, &reports, styled)
88}
89
90/// Format check errors without ANSI colors (for testing).
91///
92/// Returns `Err` with suspension if file content isn't loaded yet.
93pub fn format_check_errors_plain(
94    db: &impl Db,
95    result: &CheckResult,
96    file: TextFile,
97    cst: &Cst,
98    origins: &OriginMap,
99) -> Result<String, QueryError> {
100    format_check_errors(db, result, file, cst, origins, false)
101}