vize_l1 0.492.0

L1 - the lossless Vue-template surface tree (codename Sinopia, Davinci P2-7)
Documentation
use super::{assert_own_original_root, selected};
use crate::embed::{Grammar, Lang, Shape};
use crate::markup::{
    ArgSyntax, DirectiveName, DirectivePrefix, NativeAttributeOperandError, NativeConditionKind,
};
use oxc_ast::ast::Expression;
use vize_l0::{Allocator, Span};

#[test]
fn unicode_nonzero_name_block_parts_and_actual_ordinal_preserve_both_original_routes() {
    let arena = Allocator::default();
    let source = "<!--雪🌸-->\r\n<template><p id=x v-else-if='条件'/></template>";
    let old_owner = selected(&arena, source);
    let new_owner = selected(&arena, source);
    let old_element = old_owner.children().next().unwrap().into_element().unwrap();
    let new_element = new_owner.children().next().unwrap().into_element().unwrap();
    let attribute = new_element.attributes().nth(1).unwrap();
    let head = new_owner
        .observe_attribute_head(attribute.reborrow())
        .unwrap();
    assert!(core::ptr::eq(head.selected(), &new_owner));
    assert!(core::ptr::eq(
        head.attribute().surface(),
        attribute.surface()
    ));
    assert!(core::ptr::eq(
        head.attribute().element(),
        new_element.surface()
    ));
    assert_eq!(head.attribute().ordinal(), 1);
    assert_eq!(head.name_block().span(), Span::new(34, 43));
    assert_eq!(head.name_block().source(), "v-else-if");
    assert!(core::ptr::eq(head.name_block().root_source(), source));
    assert!(core::ptr::eq(
        head.name_block().source(),
        attribute.surface().name.text
    ));
    assert_eq!(
        head.directive(),
        Some(DirectiveName {
            prefix: DirectivePrefix::Full,
            name: Span::new(36, 43),
            arg: None,
            modifiers: Span::new(43, 43),
        })
    );
    assert_eq!(head.condition_kind(), Some(NativeConditionKind::ElseIf));

    let old = old_owner
        .observe_attribute_expression(old_element.attributes().nth(1).unwrap())
        .unwrap();
    let new = head.observe_expression().unwrap();
    for operand in [&old, &new] {
        assert_eq!(operand.kind(), NativeConditionKind::ElseIf);
        assert_eq!(operand.name_span(), Span::new(34, 43));
        assert_eq!(operand.value_span(), Span::new(45, 51));
        assert_eq!(operand.raw_value(), "条件");
        assert_eq!(operand.syntax().source().text(), "条件");
        assert_eq!(operand.syntax().source().span(), Span::new(45, 51));
        assert!(operand.syntax().source().decode_map().is_none());
        assert!(core::ptr::eq(
            operand.syntax().source().text(),
            operand.raw_value()
        ));
        assert_eq!(operand.syntax().parser_prefix(), 2);
        assert_eq!(
            operand.syntax().grammar(),
            Grammar {
                shape: Shape::Expr,
                lang: Lang::Js
            }
        );
        assert_eq!(operand.syntax().hole(), None);
        assert_eq!(operand.syntax().comments().count(), 0);
        assert_eq!(operand.syntax().diagnostics().count(), 0);
        let Expression::Identifier(identifier) = operand.syntax().expression().unwrap() else {
            panic!("the actual original Unicode identifier must be retained")
        };
        assert_eq!(identifier.name.as_str(), "条件");
        assert_eq!(identifier.span, oxc_span::Span::new(2, 8));
        assert_eq!(
            operand.syntax().decoded_span(identifier.span),
            Ok(Span::new(0, 6))
        );
        assert_eq!(
            operand.syntax().authored_span(identifier.span),
            Ok(Span::new(45, 51))
        );
    }
    assert_own_original_root(&old, &old_owner, 1);
    assert_own_original_root(&new, &new_owner, 1);
    assert!(!core::ptr::eq(
        old.syntax().expression().unwrap(),
        new.syntax().expression().unwrap()
    ));
}

#[test]
fn plain_static_dynamic_and_all_shorthand_headers_keep_complete_typed_parts() {
    let arena = Allocator::default();
    let source = "<template><p title=x v-bind:雪.prop='x' v-on:[event].once='x' :id='x' .value='x' @click='x' #default='x' v-if='ready'/></template>";
    let old_owner = selected(&arena, source);
    let new_owner = selected(&arena, source);
    let old_element = old_owner.children().next().unwrap().into_element().unwrap();
    let new_element = new_owner.children().next().unwrap().into_element().unwrap();
    let cases = [
        ("title", Span::new(13, 18), None),
        (
            "v-bind:雪.prop",
            Span::new(21, 36),
            Some(DirectiveName {
                prefix: DirectivePrefix::Full,
                name: Span::new(23, 27),
                arg: Some(ArgSyntax::Static(Span::new(28, 31))),
                modifiers: Span::new(31, 36),
            }),
        ),
        (
            "v-on:[event].once",
            Span::new(41, 58),
            Some(DirectiveName {
                prefix: DirectivePrefix::Full,
                name: Span::new(43, 45),
                arg: Some(ArgSyntax::Dynamic(Span::new(47, 52))),
                modifiers: Span::new(53, 58),
            }),
        ),
        (
            ":id",
            Span::new(63, 66),
            Some(DirectiveName {
                prefix: DirectivePrefix::Bind,
                name: Span::new(63, 63),
                arg: Some(ArgSyntax::Static(Span::new(64, 66))),
                modifiers: Span::new(66, 66),
            }),
        ),
        (
            ".value",
            Span::new(71, 77),
            Some(DirectiveName {
                prefix: DirectivePrefix::Prop,
                name: Span::new(71, 71),
                arg: Some(ArgSyntax::Static(Span::new(72, 77))),
                modifiers: Span::new(77, 77),
            }),
        ),
        (
            "@click",
            Span::new(82, 88),
            Some(DirectiveName {
                prefix: DirectivePrefix::On,
                name: Span::new(82, 82),
                arg: Some(ArgSyntax::Static(Span::new(83, 88))),
                modifiers: Span::new(88, 88),
            }),
        ),
        (
            "#default",
            Span::new(93, 101),
            Some(DirectiveName {
                prefix: DirectivePrefix::Slot,
                name: Span::new(93, 93),
                arg: Some(ArgSyntax::Static(Span::new(94, 101))),
                modifiers: Span::new(101, 101),
            }),
        ),
        (
            "v-if",
            Span::new(106, 110),
            Some(DirectiveName {
                prefix: DirectivePrefix::Full,
                name: Span::new(108, 110),
                arg: None,
                modifiers: Span::new(110, 110),
            }),
        ),
    ];
    assert_eq!(old_element.attributes().len(), 8);
    assert_eq!(new_element.attributes().len(), 8);
    for (ordinal, (raw, span, parts)) in cases.into_iter().enumerate() {
        let attribute = new_element.attributes().nth(ordinal).unwrap();
        let before = arena.allocated_bytes();
        let head = new_owner
            .observe_attribute_head(attribute.reborrow())
            .unwrap();
        assert_eq!(arena.allocated_bytes(), before);
        assert_eq!(head.name_block().span(), span);
        assert_eq!(head.name_block().source(), raw);
        assert!(core::ptr::eq(head.name_block().root_source(), source));
        assert!(core::ptr::eq(head.selected(), &new_owner));
        assert!(core::ptr::eq(
            head.attribute().surface(),
            attribute.surface()
        ));
        assert_eq!(head.attribute().ordinal(), ordinal);
        assert_eq!(head.directive(), parts);
        let old =
            old_owner.observe_attribute_expression(old_element.attributes().nth(ordinal).unwrap());
        let new = head.observe_expression();
        if ordinal == 7 {
            assert_eq!(head.condition_kind(), Some(NativeConditionKind::If));
            let old = old.unwrap();
            let new = new.unwrap();
            for operand in [&old, &new] {
                assert_eq!(operand.name_span(), Span::new(106, 110));
                assert_eq!(operand.value_span(), Span::new(112, 117));
                assert_eq!(operand.raw_value(), "ready");
                assert_eq!(operand.syntax().source().text(), "ready");
                assert_eq!(operand.syntax().source().span(), Span::new(112, 117));
                assert_eq!(operand.syntax().hole(), None);
                assert_eq!(operand.syntax().comments().count(), 0);
                assert_eq!(operand.syntax().diagnostics().count(), 0);
                let Expression::Identifier(identifier) = operand.syntax().expression().unwrap()
                else {
                    panic!("the exact conditional header retains its original ready identifier")
                };
                assert_eq!(identifier.name.as_str(), "ready");
                assert_eq!(identifier.span, oxc_span::Span::new(2, 7));
                assert_eq!(
                    operand.syntax().authored_span(identifier.span),
                    Ok(Span::new(112, 117))
                );
            }
            assert_own_original_root(&old, &old_owner, ordinal);
            assert_own_original_root(&new, &new_owner, ordinal);
            assert!(!core::ptr::eq(
                old.syntax().expression().unwrap(),
                new.syntax().expression().unwrap()
            ));
        } else {
            assert_eq!(head.condition_kind(), None);
            for failure in [old.err().unwrap(), new.err().unwrap()] {
                assert_eq!(
                    failure.kind(),
                    NativeAttributeOperandError::UnsupportedDirective
                );
                assert!(failure.syntax().is_none());
            }
        }
    }
}