vize_l4 0.489.0

L4 - the Davinci emission level (codename Stesura): writer, expression rewriting, module assembly and targets
Documentation
use super::{EmitDocument, SpanLink, expression_links};
use crate::write::source_map::SourceMapBuilder;
use vize_l0::{Span, String};

fn link(generated: (u32, u32), authored: (u32, u32), name: Option<&str>) -> SpanLink {
    SpanLink {
        generated: Span::new(generated.0, generated.1),
        authored: Span::new(authored.0, authored.1),
        name: name.map(String::new),
        segment: true,
    }
}

fn container(generated: (u32, u32), authored: (u32, u32)) -> SpanLink {
    SpanLink {
        segment: false,
        ..link(generated, authored, None)
    }
}

#[test]
fn expression_links_carry_whole_and_identifier_ranges() {
    let source = "{{ a + b }} {{ 1 + c }}";
    assert_eq!(
        expression_links("_ctx.a + _ctx.b", Span::new(3, 8), source),
        vec![
            container((0, 15), (3, 8)),
            link((0, 6), (3, 4), Some("a")),
            link((9, 15), (7, 8), Some("b")),
        ]
    );
    assert_eq!(
        expression_links("1 + _ctx.c", Span::new(15, 20), source),
        vec![
            link((0, 10), (15, 20), None),
            link((4, 10), (19, 20), Some("c")),
        ]
    );
    assert_eq!(expression_links("x", Span::new(0, 0), source), vec![]);
}

#[test]
fn fragments_rebase_their_links() {
    let source = "<p :title=\"t\">";
    let mut value = EmitDocument::plain("_ssrRenderAttr(\"");
    value.push_mapped("title", 4);
    value.push_str("\", ");
    value.push_expression("_ctx.t", Span::new(11, 12), source);
    value.push_str(")");
    let mut part = EmitDocument::plain("<p");
    part.push_spanned(&value);
    assert_eq!(part.as_str(), "<p_ssrRenderAttr(\"title\", _ctx.t)");
    assert_eq!(
        part.links(),
        [
            link((18, 23), (4, 4), None),
            container((26, 32), (11, 12)),
            link((26, 32), (11, 12), Some("t")),
        ]
    );
}

#[test]
fn escaping_rebases_links_onto_the_escaped_output() {
    let mut piece = EmitDocument::default();
    piece.push_mapped("a`b", 10);
    piece.push_linked("${c}", Span::new(20, 24));
    piece.push_mapped("`", 30);
    let mut out = EmitDocument::plain("x");
    out.push_escaped(&piece, &[("`", "\\`"), ("${", "\\${")]);
    assert_eq!(out.as_str(), "xa\\`b\\${c}\\`");
    assert_eq!(
        out.links(),
        [
            link((1, 5), (10, 10), None),
            link((5, 10), (20, 24), None),
            link((10, 12), (30, 30), None),
        ]
    );
}

#[test]
fn escaping_skips_empty_patterns_and_clamps_shrunken_links() {
    let mut piece = EmitDocument::default();
    piece.push_mapped("ab", 10);
    piece.anchor(11);
    piece.push_mapped("cd", 12);

    let mut out = EmitDocument::plain("pre");
    out.push_escaped(&piece, &[("", "ignored"), ("abcd", "x")]);
    assert_eq!(out.as_str(), "prex");
    assert_eq!(
        out.links(),
        [
            link((3, 3), (10, 10), None),
            link((3, 3), (11, 11), None),
            link((3, 4), (12, 12), None),
        ]
    );
}

#[test]
fn insertion_shifts_the_links_at_or_after_it() {
    let mut doc = EmitDocument::new(true);
    doc.push_linked("ab", Span::new(0, 2));
    doc.anchor(5);
    doc.push_linked("cd", Span::new(10, 12));
    doc.insert_str(2, "XY");
    assert_eq!(doc.as_str(), "abXYcd");
    assert_eq!(
        doc.links(),
        [
            link((0, 2), (0, 2), None),
            link((4, 4), (5, 5), None),
            link((4, 6), (10, 12), None),
        ]
    );
}

#[test]
fn a_document_that_does_not_record_keeps_identical_text_and_no_links() {
    let build = |recording: bool| {
        let mut doc = EmitDocument::new(recording);
        doc.anchor(0);
        doc.push_str("function render(_ctx) {\n  return ");
        doc.push_expression("_ctx.msg", Span::new(3, 6), "{{ msg }}");
        doc.push_named("id", Span::new(1, 3), "id");
        doc.push_escaped(&EmitDocument::mapped("`", 7), &[("`", "\\`")]);
        doc
    };
    let (on, off) = (build(true), build(false));
    assert_eq!(on.as_str(), off.as_str());
    assert!(off.links().is_empty());
    assert_eq!(on.links().len(), 5);
}

#[test]
fn source_map_equals_the_builder_fed_the_same_anchors() {
    let source = "<p id=\"x\">{{ a + b }}</p>";
    let mut doc = EmitDocument::new(true);
    doc.anchor(0);
    doc.push_str("return ");
    doc.push_mapped("\"p\"", 1);
    doc.push_str(", { ");
    doc.push_named("id", Span::new(3, 5), "id");
    doc.push_str(": ");
    doc.push_linked("\"x\"", Span::new(7, 8));
    doc.push_str(" }, ");
    doc.push_expression("_ctx.a + _ctx.b", Span::new(13, 18), source);

    let mut builder = SourceMapBuilder::new();
    builder.add_raw(0, 0);
    builder.add_raw(7, 1);
    builder.add_named(14, 3, "id");
    builder.add_raw(18, 7);
    builder.add_named(25, 13, "a");
    builder.add_named(34, 17, "b");
    let expected = builder.finish(doc.as_str(), "t.vue", source);
    assert_eq!(doc.source_map("t.vue", source), expected);
}