laburnum 1.17.3

An LSP framework for building language servers and compilers, powered by an incremental query tree with content-addressed storage, task-based dataflow, and parallel queries.
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

/// Declares a set of known identifiers as compile-time constants.
///
/// This macro generates `pub const` declarations for [`Spanned<Ident>`] values
/// and a `try_resolve` function to look up the original string from an ident
/// hash.
///
/// # Usage
///
/// Create a dedicated `{language}-known-idents` crate for your language's
/// known identifiers. This keeps them centralized and allows both the compiler
/// and LSP to share the same definitions.
///
/// ```ignore
/// // In your {language}-known-idents/src/lib.rs
/// use laburnum::known_idents;
///
/// known_idents! {
///     // Simple identifiers: input becomes the string value,
///     // UPPER_CASE version becomes the const name
///     theme,           // pub const THEME: Spanned<Ident>
///     palette,         // pub const PALETTE: Spanned<Ident>
///     my_field,        // pub const MY_FIELD: Spanned<Ident>
///
///     // Explicit overrides for strings that aren't valid Rust identifiers
///     // or need different const names (e.g., PascalCase, kebab-case)
///     {
///         COLOR_HEX = "ColorHex",
///         TEMPLATES_PER_VARIANT = "templates-per-variant",
///     }
/// }
/// ```
///
/// # Generated Code
///
/// The macro generates:
/// - `pub const NAME: Spanned<Ident>` for each identifier, pairing the ident
///   with a `const` span into the reserved known-idents source
/// - `pub const BLOB: &str`, the idents concatenated in layout order
/// - `pub const fn try_resolve_spanned(Ident) -> Option<Spanned<Ident>>`,
///   returning a `const` span into the reserved known-idents source
/// - `pub const fn try_resolve(Ident) -> Option<&'static str>` for text lookup
///
/// Because the known idents are always the first source registered in the
/// cache, both their `file_id`
/// ([`KNOWN_IDENTS_FILE_ID`](crate::KNOWN_IDENTS_FILE_ID)) and their byte
/// offsets in `BLOB` are fixed at compile time, so every span is a constant.
///
/// # Crate Structure
///
/// Recommended structure for a language called "mylang":
///
/// ```text
/// crates/
///   mylang-known-idents/
///     Cargo.toml
///     src/lib.rs        # known_idents! { ... }
/// ```
///
/// Your `Cargo.toml` must include both `laburnum` and `paste`:
///
/// ```toml
/// [dependencies]
/// laburnum = { workspace = true }
/// paste = { workspace = true }
/// ```
///
/// Then depend on `mylang-known-idents` from your compiler and LSP crates.
#[macro_export]
macro_rules! known_idents {
    // Public entry: a language's known idents. Minted through the gated
    // `Ident::new` and laid out in the reserved [`KNOWN_IDENTS_FILE_ID`] source.
    (
        $( $name:ident ),* $(,)?
        $(
            { $( $const_name:ident = $string:literal ),+ $(,)? }
        )?
    ) => {
        $crate::known_idents! {
            @impl
            file_id = $crate::KNOWN_IDENTS_FILE_ID,
            ctor = ($crate::Ident::new_const),
            $( $name ),*
            $( { $( $const_name = $string ),+ } )?
        }
    };

    // Implementation arm, parameterized by the source `file_id` the spans point
    // into and the constructor used to mint the idents. The crate-internal
    // partition-key table calls this directly with `Ident::new_const_internal`
    // and [`LABURNUM_INTERNAL_KNOWN_IDENTS_FILE_ID`].
    (
        @impl
        file_id = $file_id:expr,
        ctor = ($ctor:path),
        $( $name:ident ),* $(,)?
        $(
            { $( $const_name:ident = $string:literal ),+ $(,)? }
        )?
    ) => {
        ::paste::paste! {
            $(
                #[allow(dead_code)]
                pub const [<$name:upper>]: $crate::SpannedIdent =
                    __known_spanned($ctor(stringify!($name)));
            )*
            $(
                $(
                    #[allow(dead_code)]
                    pub const $const_name: $crate::SpannedIdent =
                        __known_spanned($ctor($string));
                )+
            )?

            /// Pair a known ident with its `const` span, falling back to
            /// [`Span::unknown`](crate::Span::unknown) for idents outside the
            /// table (unreachable for the consts above, which are members).
            const fn __known_spanned(
                ident: $crate::Ident,
            ) -> $crate::SpannedIdent {
                match try_resolve_spanned(ident) {
                    ::core::option::Option::Some(spanned) => spanned,
                    ::core::option::Option::None => {
                        $crate::SpannedIdent::new(ident, $crate::Span::unknown())
                    }
                }
            }

            /// Every known ident, in the order their spans are laid out in
            /// [`BLOB`].
            pub const KNOWN_IDENT_KEYS: &[$crate::Ident] = &[
                $( $ctor(stringify!($name)), )*
                $( $( $ctor($string), )+ )?
            ];

            /// The text of every known ident, parallel to [`KNOWN_IDENT_KEYS`].
            pub const KNOWN_IDENT_STRS: &[&str] = &[
                $( stringify!($name), )*
                $( $( $string, )+ )?
            ];

            /// Every known ident concatenated in layout order. Registered as
            /// the reserved known-idents source so the spans below resolve.
            pub const BLOB: &str = concat!(
                $( stringify!($name), )*
                $( $( $string, )+ )?
            );

            /// Resolve an ident to its known `SpannedIdent`, with a `const`
            /// span into the known-idents source.
            pub const fn try_resolve_spanned(
                ident: $crate::Ident,
            ) -> Option<$crate::SpannedIdent> {
                let mut i = 0;
                let mut offset = 0u32;
                while i < KNOWN_IDENT_KEYS.len() {
                    let len = KNOWN_IDENT_STRS[i].len() as u32;
                    if KNOWN_IDENT_KEYS[i].const_eq(ident) {
                        // SAFETY: `$file_id` is the real frozen known-idents
                        // source this table is registered under, and
                        // (offset, len) is this ident's exact byte range in
                        // that compile-time blob.
                        let span = unsafe {
                            $crate::Span::new_inline_unchecked(
                                $file_id,
                                0,
                                offset,
                                len,
                            )
                        };
                        return Some($crate::SpannedIdent::new(
                            KNOWN_IDENT_KEYS[i],
                            span,
                        ));
                    }
                    offset += len;
                    i += 1;
                }
                None
            }

            /// Resolve an ident to its known text.
            #[allow(dead_code)]
            pub const fn try_resolve(ident: $crate::Ident) -> Option<&'static str> {
                let mut i = 0;
                while i < KNOWN_IDENT_KEYS.len() {
                    if KNOWN_IDENT_KEYS[i].const_eq(ident) {
                        return Some(KNOWN_IDENT_STRS[i]);
                    }
                    i += 1;
                }
                None
            }
        }
    };
}

/// Declares laburnum's own partition-key known idents.
///
/// Same generated surface as [`known_idents!`], but mints idents through the
/// crate-internal `Ident::new_const_internal` (so it needs no `ident_constructor`
/// feature) and lays the spans out in the reserved
/// [`LABURNUM_INTERNAL_KNOWN_IDENTS_FILE_ID`](crate::LABURNUM_INTERNAL_KNOWN_IDENTS_FILE_ID)
/// source.
macro_rules! known_idents_internal {
    ( $( $tt:tt )* ) => {
        $crate::known_idents! {
            @impl
            file_id = $crate::LABURNUM_INTERNAL_KNOWN_IDENTS_FILE_ID,
            ctor = ($crate::Ident::new_const_internal),
            $( $tt )*
        }
    };
}
pub(crate) use known_idents_internal;

#[cfg(test)]
mod tests {
  // Exercise the macro the way a downstream `{language}-known-idents` crate
  // would, then check the const layout it produces.
  mod ki {
    crate::known_idents! {
      theme,
      palette,
      {
        SELF_KW = "self",
        COLOR_HEX = "ColorHex",
      }
    }
  }

  use crate::{Ident, KNOWN_IDENTS_FILE_ID, SpanCache};

  #[test]
  fn blob_concatenates_in_layout_order() {
    assert_eq!(ki::BLOB, "themepaletteselfColorHex");
  }

  #[test]
  fn spanned_resolution_uses_const_offsets() {
    // SAFETY: this test deliberately exercises the known-idents synthetic
    // source, so KNOWN_IDENTS_FILE_ID is the correct, real id here.
    let cache = unsafe { SpanCache::new(KNOWN_IDENTS_FILE_ID, 0) };

    // palette follows theme (offset 5), is 7 bytes long. The const itself is
    // now a `SpannedIdent`.
    let ident = ki::PALETTE.ident();
    let span = ki::PALETTE.span();
    assert_eq!(ident, Ident::new("palette"));
    assert_eq!(span.file_id(), KNOWN_IDENTS_FILE_ID);
    let data = span.data(&cache).unwrap();
    assert_eq!(data.start, 5);
    assert_eq!(data.len, 7);

    // The span's bytes slice the blob back to the original text.
    assert_eq!(span.text(&cache, ki::BLOB), Some("palette"));

    // The override entry "self" lands after "themepalette" (offset 12).
    let self_span = ki::SELF_KW.span();
    assert_eq!(self_span.text(&cache, ki::BLOB), Some("self"));
  }

  #[test]
  fn text_resolution_and_misses() {
    assert_eq!(ki::try_resolve(ki::COLOR_HEX.ident()), Some("ColorHex"));
    assert_eq!(ki::try_resolve(Ident::new("nope")), None);
    assert!(ki::try_resolve_spanned(Ident::new("nope")).is_none());
  }

  #[test]
  fn known_idents_compose_as_a_const() {
    // The whole point: a known ident is itself a `const SpannedIdent`.
    const PALETTE_SPANNED: crate::SpannedIdent = ki::PALETTE;
    assert_eq!(PALETTE_SPANNED.ident(), Ident::new("palette"));
  }
}