laburnum-syntax-macro 0.1.0

Proc-macros for defining CST and AST node types in language frontends built with the laburnum LSP framework.
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

use super::*;

pub(super) fn bluegum_map_field_quote(
  f: &crate::ast::parse::Field,
) -> TokenStream {
  let field_name: syn::Ident = syn::parse_str(&f.name).unwrap();

  let field = {
    if f.is_field {
      match &f.ty {
        | FieldType::Path(_ty) => {
          quote! {
            use {owo_colors::OwoColorize, unicode_width::UnicodeWidthStr};
            b.field(stringify!(#field_name), format!("{:?}", #field_name).purple().italic().to_string());
          }
        },
        | FieldType::Literal(_val) => {
          quote! {
            use {owo_colors::OwoColorize, unicode_width::UnicodeWidthStr};
            b.field(stringify!(#field_name), format!("{:?}", #field_name).purple().italic().to_string());
          }
        },
        | FieldType::String => {
          quote! {
            use {owo_colors::OwoColorize, unicode_width::UnicodeWidthStr};
            b.field(stringify!(#field_name), format!("{:?}", #field_name).purple().italic().to_string());
          }
        },
        | FieldType::Enum(_val) => {
          quote! {
            use {owo_colors::OwoColorize, unicode_width::UnicodeWidthStr};
            b.field(stringify!(#field_name), format!("{:?}", #field_name).purple().italic().to_string());
          }
        },
        | _ => {
          quote! {
            #field_name.node_with_state(b,state);
          }
        },
      }
    } else if f.is_vec {
      quote! {
        b.add_nodes_of_builders(
          stringify!(#field_name),

         #field_name
          .iter()
          .map(|child| bluegum::Builder::render_with_state(child, state))
          .collect::<Vec<bluegum::Builder>>(),
        );
      }
    } else {
      match f.ty {
        | FieldType::Error(ref val) => {
          // Error fields indicate a macro parsing failure - generate compile error
          let error_msg = format!("Invalid field type for bluegum: {}", val);
          quote! {
            compile_error!(#error_msg);
          }
        },
        | FieldType::SingleNode(ref _val) => {
          quote! {
            b.add_node_with_state(state, stringify!(#field_name), #field_name);
          }
        },
        | FieldType::MultipleNode(ref _types) => {
          quote! {
            b.add_node_with_state(state, stringify!(#field_name), #field_name);
          }
        },
        | FieldType::EnumNodeId(ref val) => {
          let enum_ty = syn::parse_str::<syn::Path>(val).unwrap();

          quote! {
              match state.get(*#field_name) {
                | Some(node) => {
                  b.add_node_with_state(state, stringify!(#field_name), &#enum_ty::from(node));
                },
                | None => {
                  b.field(&format!("Node not found: {self}"), "None");
                },
              }
          }
        },
        | FieldType::Path(ref _ty) => {
          quote! {
            b.add_node_with_state(state, stringify!(#field_name), #field_name);
          }
        },
        | FieldType::Literal(val) => {
          quote! {
            b.field(stringify!(#field_name), #val.green());

            let s = format!("{}", #field_name);
            if s.width() > 80 {
              b.alt(format!("{:.80}...", s).green());
            } else {
              b.alt(format!("{:.80}", s).green());
            };
          }
        },
        | FieldType::String => {
          quote! {
            use {owo_colors::OwoColorize, unicode_width::UnicodeWidthStr};
            b.debug(stringify!(#field_name), format!("{:?}", #field_name));
            let val = state.get_interned(#field_name).to_string();
            if val.width() > 80 {
              b.alt(format!("{:.80}...", val).bright_white());
            } else {
              b.alt(format!("{:.80}", val).bright_white());
            };
          }
        },
        | FieldType::Span => {
          quote! {
            use {owo_colors::OwoColorize, unicode_width::UnicodeWidthStr};
            let val = #field_name.text(state.get_span_cache(), state.get_source()).unwrap_or("");
            b.field(stringify!(#field_name), "Span".cyan().to_string());
            if val.width() > 80 {
              b.alt(format!("{:.80}...", val).bright_white());
            } else {
              b.alt(format!("{:.80}", val).bright_white());
            };
          }
        },
        | FieldType::Ident => {
          quote! {
            use {owo_colors::OwoColorize, unicode_width::UnicodeWidthStr};
            // Ident without span - can only show hash
            b.field(stringify!(#field_name), format!("{:?}", #field_name).cyan().to_string());
          }
        },
        | FieldType::SpannedIdent => {
          quote! {
            use {owo_colors::OwoColorize, unicode_width::UnicodeWidthStr};
            let (ident, span) = #field_name;
            let val = span.text(state.get_span_cache(), state.get_source()).unwrap_or("");
            b.field(stringify!(#field_name),"".to_string()).alt(val.bright_white().to_string());
          }
        },
        | FieldType::Enum(_) => {
          quote! {
            b.add_node_with_state(state, stringify!(#field_name), #field_name);
          }
        },

        | _ => {
          quote! {}
        },
      }
    }
  };

  if f.is_optional {
    quote! {
      {
        if let Some(ref #field_name) = self.#field_name {
          #field
        }
      };
    }
  } else {
    quote! {
      {
        let #field_name = &self.#field_name;
        #field
      };
    }
  }
}