buntrail-engine 0.2.0

Authoritative document and workspace orchestration.
Documentation
//! Legacy response adapter over the imported cached language implementation.
use super::{Result, fail};
use buntrail_core::{Snapshot, contracts::*};
use buntrail_language::LanguageAdapter;
use serde_json::{Value, json};

pub(super) fn canonical(name: &str) -> Result<&'static str> {
    let name = match name {
        "mjs" | "cjs" => "javascript",
        "mts" | "cts" => "typescript",
        "h" => "c",
        "cc" | "hpp" | "hxx" => "cpp",
        "htm" => "html",
        "svg" => "xml",
        other => other,
    };
    buntrail_language::registry::alias(name).ok_or_else(|| {
        fail(
            "unsupported_language",
            format!(
                "Structural grammar unavailable for {name}; plain text editing remains available"
            ),
        )
    })
}
fn outline(node: &OutlineNode) -> Value {
    json!({"id":node.id,"parentID":node.parent_id,"kind":node.kind,"label":node.label,"preview":node.preview,"selection":{"start":node.selection.start,"end":node.selection.end},"enclosing":{"start":node.range.start,"end":node.range.end},"children":node.children.iter().map(outline).collect::<Vec<_>>()})
}
fn symbols(nodes: &[OutlineNode], out: &mut Vec<Value>) {
    for node in nodes {
        out.push(json!({"name":node.label,"start":node.range.start,"end":node.range.end,"kind":node.kind}));
        symbols(&node.children, out);
    }
}
pub(super) fn analyze(
    adapter: &LanguageAdapter,
    id: u64,
    snapshot: Snapshot,
    name: &str,
) -> Result<Value> {
    let canonical = canonical(name)?;
    if snapshot.text.len() > 1024 * 1024 {
        return Err(fail(
            "analysis_limit",
            "Structural analysis is disabled above 1 MiB; text remains editable",
        ));
    }
    let mut document = [0; 16];
    document[..8].copy_from_slice(&id.to_le_bytes());
    let input = DocumentSnapshot {
        document: DocumentID(document),
        revision: BufferRevision(snapshot.revision),
        text: snapshot.text.clone(),
        encoding: Encoding::Utf8 { bom: false },
        line_endings: LineEndings::Lf,
        language: Some(canonical.into()),
    };
    let analyzed =
        adapter.analyze_snapshot_with_limits(input, std::time::Duration::from_millis(300), 20_000);
    if let LanguageAvailability::Unavailable(error) = &analyzed.availability {
        return Err(fail("analysis_limit", error.message.clone()));
    }
    let mut names = vec![];
    symbols(&analyzed.outline, &mut names);
    let mut starts = vec![0];
    for (offset, unit) in snapshot.text.as_str().encode_utf16().enumerate() {
        if unit == 10 {
            starts.push(offset as u64 + 1);
        }
    }
    let line = |offset: u64| {
        starts
            .partition_point(|start| *start <= offset)
            .saturating_sub(1)
    };
    let highlights = analyzed
        .highlights
        .iter()
        .take(20_000)
        .map(|h| json!({"start":h.range.start,"end":h.range.end,"kind":h.scope}))
        .collect::<Vec<_>>();
    let folds=analyzed.folds.iter().map(|f|json!({"start":f.range.start,"end":f.range.end,"line":line(f.range.start),"endLine":line(f.range.end),"id":f.id})).collect::<Vec<_>>();
    Ok(
        json!({"id":id,"revision":snapshot.revision,"language":name,"highlights":highlights,"folds":folds,"symbols":names,"hasErrors":analyzed.has_errors,"truncated":analyzed.truncated || analyzed.highlights.len()>20_000,"grammar":analyzed.grammar_id,"queryVersion":analyzed.query_version,"available":true,"outline":analyzed.outline.iter().map(outline).collect::<Vec<_>>(),"injections":analyzed.injections.iter().map(|i|json!({"start":i.range.start,"end":i.range.end,"language":i.language})).collect::<Vec<_>>()}),
    )
}