Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: initial implementation of key value block attributes #49

Merged
merged 6 commits into from
Jul 28, 2024

Conversation

tachibanayui
Copy link

This is my initial attempt at implementing block support for the Key-value attribute described in #48 by adding another variant of NodeBlock into KeyedAttributeValue. I also implement ParseRecoverable trait for KeyedAttribute so that transform_block() will work inside key-value attributes.

// Placeholder, implement this to meet your requirements
let parse_cfg = ParserConfig::new().transform_block(|input| {
    println!("handled");
    dbg!(input);
    while let Ok(_) = input.parse::<TokenTree>() {}
    return Ok(Some(quote!("handled").into()));
});

let token= quote!(
    <Route
        style={{ width: "10vw" }}
        view={<MyView />}
        ext={anything is possible here!}></Route>
);
dbg!(Parser::new(parse_cfg).parse_simple(token));

Copy link
Collaborator

@vldm vldm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thank you for contribution.

Since you anyway plan to use transform_block why you need using of NodeBlock at all?
The aim of this enum is to save blocks of unparsable rust code aka InvalidBlock mainly for IDE purposes (or better error reporting).
It also can be used as replacement for transform_block in some cases.

But if we decide to keep NodeBlock in position of attribute value, i'd recommend to rename this type.

src/node/attribute.rs Show resolved Hide resolved
src/node/attribute.rs Outdated Show resolved Hide resolved
@tachibanayui
Copy link
Author

Hi, thank you for contribution.

Since you anyway plan to use transform_block why you need using of NodeBlock at all? The aim of this enum is to save blocks of unparsable rust code aka InvalidBlock mainly for IDE purposes (or better error reporting). It also can be used as replacement for transform_block in some cases.

But if we decide to keep NodeBlock in position of attribute value, i'd recommend to rename this type.

From the rustdoc of NodeBlock, I thought it is a struct to store parsed block or invalid block, and its implementation also handles other cases like recover_block so I think it is reasonable to reuse NodeBlock. As for renaming, I would suggest FuzzyBlock

I'd recommend to move of handling invalid block into AttributeValueExpr, for two reasons:

I don't get what you mean. Should I implement this inside AttributeValueExpr::parse_recoverable?

// Code copied from NodeBlock::parse_recoverable
Err(e) if parser.config().recover_block => {
    parser.push_diagnostic(e);
    NodeBlock::Invalid(parser.parse_simple(input)?)
}
Err(e) => {
    parser.push_diagnostic(e);
    return None;
}

@vldm
Copy link
Collaborator

vldm commented Feb 14, 2024

@tachibanayui

I'd recommend to move of handling invalid block into AttributeValueExpr, for two reasons:

I don't get what you mean. Should I implement this inside AttributeValueExpr::parse_recoverable?

You can simply add transform_block logic into ParseRecoverable implementation of AttributeValueExpr. Just do an early check if it is a block, after block transformation you will always get a valid syn::Block that can be wrapped into syn::Expr.

You don't need to implement recover_block logic in the position of attribute value.
We can move this implementation into a separate task along with refining what rust expressions that can be used at the attribute value position.

@tachibanayui
Copy link
Author

I have updated the implementation to reflect the discussion in #54.
I also wanted to rename a few structs but hesitated because it would lead to a breaking change:

  • AttributeValueExpr -> AttributeValue
  • KVAttributeValue -> AttrValue (I'm also not sure how to name this)

Demos:

  1. Struct initialization syntax (Allow struct initialzation syntax inside attribute #48), similar to how the style prop work in React:
<MyComponent style={{ width: "20vw" }} />
Parse result:
NodeElement {
    open_tag: OpenTag {
        token_lt: Lt,
        name: Path(
            ExprPath {
                attrs: [],
                qself: None,
                path: Path {
                    leading_colon: None,
                    segments: [
                        PathSegment {
                            ident: Ident {
                                sym: MyComponent,
                            },
                            arguments: PathArguments::None,
                        },
                    ],
                },
            },
        ),
        generics: Generics {
            lt_token: None,
            params: [],
            gt_token: None,
            where_clause: None,
        },
        attributes: [
            Attribute(
                KeyedAttribute {
                    key: Path(
                        ExprPath {
                            attrs: [],
                            qself: None,
                            path: Path {
                                leading_colon: None,
                                segments: [
                                    PathSegment {
                                        ident: Ident {
                                            sym: style,
                                        },
                                        arguments: PathArguments::None,
                                    },
                                ],
                            },
                        },
                    ),
                    possible_value: Value(
                        KeyValueAttribute {
                            token_eq: Eq,
                            value: Braced(
                                InvalidBlock {
                                    brace: Brace,
                                    body: TokenStream [
                                        Group {
                                            delimiter: Brace,
                                            stream: TokenStream [
                                                Ident {
                                                    sym: width,
                                                },
                                                Punct {
                                                    char: ':',
                                                    spacing: Alone,
                                                },
                                                Literal {
                                                    lit: "20vw",
                                                    span: bytes(1..7),
                                                },
                                            ],
                                        },
                                    ],
                                },
                            ),
                        },
                    ),
                },
            ),
        ],
        end_tag: OpenTagEnd {
            token_solidus: Some(
                Slash,
            ),
            token_gt: Gt,
        },
    },
    children: [],
    close_tag: None,
},
  1. Component in attribute syntax, React Suspend example:
<Suspend fallback={<div> Loading </div>}>
    <MyView />
</Suspend>
Parse result:
NodeElement {
    open_tag: OpenTag {
        token_lt: Lt,
        name: Path(
            ExprPath {
                attrs: [],
                qself: None,
                path: Path {
                    leading_colon: None,
                    segments: [
                        PathSegment {
                            ident: Ident {
                                sym: Suspend,
                            },
                            arguments: PathArguments::None,
                        },
                    ],
                },
            },
        ),
        generics: Generics {
            lt_token: None,
            params: [],
            gt_token: None,
            where_clause: None,
        },
        attributes: [
            Attribute(
                KeyedAttribute {
                    key: Path(
                        ExprPath {
                            attrs: [],
                            qself: None,
                            path: Path {
                                leading_colon: None,
                                segments: [
                                    PathSegment {
                                        ident: Ident {
                                            sym: fallback,
                                        },
                                        arguments: PathArguments::None,
                                    },
                                ],
                            },
                        },
                    ),
                    possible_value: Value(
                        AttributeValueExpr {
                            token_eq: Eq,
                            value: Braced(
                                InvalidBlock {
                                    brace: Brace,
                                    body: TokenStream [
                                        Punct {
                                            char: '<',
                                            spacing: Alone,
                                        },
                                        Ident {
                                            sym: div,
                                        },
                                        Punct {
                                            char: '>',
                                            spacing: Alone,
                                        },
                                        Ident {
                                            sym: Loading,
                                        },
                                        Punct {
                                            char: '<',
                                            spacing: Alone,
                                        },
                                        Punct {
                                            char: '/',
                                            spacing: Alone,
                                        },
                                        Ident {
                                            sym: div,
                                        },
                                        Punct {
                                            char: '>',
                                            spacing: Alone,
                                        },
                                    ],
                                },
                            ),
                        },
                    ),
                },
            ),
        ],
        end_tag: OpenTagEnd {
            token_solidus: None,
            token_gt: Gt,
        },
    },
    children: [
        Element(
            NodeElement {
                open_tag: OpenTag {
                    token_lt: Lt,
                    name: Path(
                        ExprPath {
                            attrs: [],
                            qself: None,
                            path: Path {
                                leading_colon: None,
                                segments: [
                                    PathSegment {
                                        ident: Ident {
                                            sym: MyView,
                                        },
                                        arguments: PathArguments::None,
                                    },
                                ],
                            },
                        },
                    ),
                    generics: Generics {
                        lt_token: None,
                        params: [],
                        gt_token: None,
                        where_clause: None,
                    },
                    attributes: [],
                    end_tag: OpenTagEnd {
                        token_solidus: Some(
                            Slash,
                        ),
                        token_gt: Gt,
                    },
                },
                children: [],
                close_tag: None,
            },
        ),
    ],
    close_tag: Some(
        CloseTag {
            start_tag: CloseTagStart {
                token_lt: Lt,
                token_solidus: Slash,
            },
            name: Path(
                ExprPath {
                    attrs: [],
                    qself: None,
                    path: Path {
                        leading_colon: None,
                        segments: [
                            PathSegment {
                                ident: Ident {
                                    sym: Suspend,
                                },
                                arguments: PathArguments::None,
                            },
                        ],
                    },
                },
            ),
            generics: Generics {
                lt_token: None,
                params: [],
                gt_token: None,
                where_clause: None,
            },
            token_gt: Gt,
        },
    ),
}

Let me know if you have any suggestions.

@vldm vldm enabled auto-merge July 26, 2024 19:12
@vldm vldm disabled auto-merge July 26, 2024 19:12
@vldm vldm changed the title initial implementation of key value block attributes feat: initial implementation of key value block attributes Jul 27, 2024
@vldm
Copy link
Collaborator

vldm commented Jul 27, 2024

@tachibanayui I have added some minor changes directly to your PR, please review, if you approve i am ready to merge it.

Forgot to mention it first, Thanks for contribution! awesome work!

@tachibanayui
Copy link
Author

Hi, I have checked your commit and it's look good to me. Thanks for approving my work!

@vldm vldm added this pull request to the merge queue Jul 28, 2024
@vldm vldm removed this pull request from the merge queue due to a manual request Jul 28, 2024
@vldm vldm merged commit e5b7f3c into rs-tml:main Jul 28, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants