use std::fmt::{self, Write};
#[cfg(feature = "ra-salsa")]
use ra_salsa::InternId;
mod ast_id;
mod hygiene;
mod map;
pub use self::{
    ast_id::{AstIdMap, AstIdNode, ErasedFileAstId, FileAstId},
    hygiene::{SyntaxContextData, SyntaxContextId, Transparency},
    map::{RealSpanMap, SpanMap},
};
pub use syntax::Edition;
pub use text_size::{TextRange, TextSize};
pub use vfs::FileId;
pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(0);
pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
    ErasedFileAstId::from_raw(!0 - 1);
pub type Span = SpanData<SyntaxContextId>;
impl Span {
    pub fn cover(self, other: Span) -> Span {
        if self.anchor != other.anchor {
            return self;
        }
        let range = self.range.cover(other.range);
        Span { range, ..self }
    }
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpanData<Ctx> {
    pub range: TextRange,
    pub anchor: SpanAnchor,
    pub ctx: Ctx,
}
impl<Ctx: fmt::Debug> fmt::Debug for SpanData<Ctx> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
            f.write_char(':')?;
            fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
            f.write_char('@')?;
            fmt::Debug::fmt(&self.range, f)?;
            f.write_char('#')?;
            self.ctx.fmt(f)
        } else {
            f.debug_struct("SpanData")
                .field("range", &self.range)
                .field("anchor", &self.anchor)
                .field("ctx", &self.ctx)
                .finish()
        }
    }
}
impl<Ctx: Copy> SpanData<Ctx> {
    pub fn eq_ignoring_ctx(self, other: Self) -> bool {
        self.anchor == other.anchor && self.range == other.range
    }
}
impl fmt::Display for Span {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
        f.write_char(':')?;
        fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
        f.write_char('@')?;
        fmt::Debug::fmt(&self.range, f)?;
        f.write_char('#')?;
        self.ctx.fmt(f)
    }
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct SpanAnchor {
    pub file_id: EditionedFileId,
    pub ast_id: ErasedFileAstId,
}
impl fmt::Debug for SpanAnchor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("SpanAnchor").field(&self.file_id).field(&self.ast_id.into_raw()).finish()
    }
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct EditionedFileId(u32);
impl fmt::Debug for EditionedFileId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("EditionedFileId").field(&self.file_id()).field(&self.edition()).finish()
    }
}
impl From<EditionedFileId> for FileId {
    fn from(value: EditionedFileId) -> Self {
        value.file_id()
    }
}
const _: () = assert!(
    EditionedFileId::RESERVED_HIGH_BITS
        + EditionedFileId::EDITION_BITS
        + EditionedFileId::FILE_ID_BITS
        == u32::BITS
);
const _: () = assert!(
    EditionedFileId::RESERVED_MASK ^ EditionedFileId::EDITION_MASK ^ EditionedFileId::FILE_ID_MASK
        == 0xFFFF_FFFF
);
impl EditionedFileId {
    pub const RESERVED_MASK: u32 = 0x8000_0000;
    pub const EDITION_MASK: u32 = 0x7F80_0000;
    pub const FILE_ID_MASK: u32 = 0x007F_FFFF;
    pub const MAX_FILE_ID: u32 = Self::FILE_ID_MASK;
    pub const RESERVED_HIGH_BITS: u32 = Self::RESERVED_MASK.count_ones();
    pub const FILE_ID_BITS: u32 = Self::FILE_ID_MASK.count_ones();
    pub const EDITION_BITS: u32 = Self::EDITION_MASK.count_ones();
    pub const fn current_edition(file_id: FileId) -> Self {
        Self::new(file_id, Edition::CURRENT)
    }
    pub const fn new(file_id: FileId, edition: Edition) -> Self {
        let file_id = file_id.index();
        let edition = edition as u32;
        assert!(file_id <= Self::MAX_FILE_ID);
        Self(file_id | (edition << Self::FILE_ID_BITS))
    }
    pub fn from_raw(u32: u32) -> Self {
        assert!(u32 & Self::RESERVED_MASK == 0);
        assert!((u32 & Self::EDITION_MASK) >> Self::FILE_ID_BITS <= Edition::LATEST as u32);
        Self(u32)
    }
    pub const fn as_u32(self) -> u32 {
        self.0
    }
    pub const fn file_id(self) -> FileId {
        FileId::from_raw(self.0 & Self::FILE_ID_MASK)
    }
    pub const fn unpack(self) -> (FileId, Edition) {
        (self.file_id(), self.edition())
    }
    pub const fn edition(self) -> Edition {
        let edition = (self.0 & Self::EDITION_MASK) >> Self::FILE_ID_BITS;
        debug_assert!(edition <= Edition::LATEST as u32);
        unsafe { std::mem::transmute(edition as u8) }
    }
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct HirFileId(u32);
impl From<HirFileId> for u32 {
    fn from(value: HirFileId) -> Self {
        value.0
    }
}
impl From<MacroCallId> for HirFileId {
    fn from(value: MacroCallId) -> Self {
        value.as_file()
    }
}
impl fmt::Debug for HirFileId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.repr().fmt(f)
    }
}
impl PartialEq<FileId> for HirFileId {
    fn eq(&self, &other: &FileId) -> bool {
        self.file_id().map(EditionedFileId::file_id) == Some(other)
    }
}
impl PartialEq<HirFileId> for FileId {
    fn eq(&self, other: &HirFileId) -> bool {
        other.file_id().map(EditionedFileId::file_id) == Some(*self)
    }
}
impl PartialEq<EditionedFileId> for HirFileId {
    fn eq(&self, &other: &EditionedFileId) -> bool {
        *self == HirFileId::from(other)
    }
}
impl PartialEq<HirFileId> for EditionedFileId {
    fn eq(&self, &other: &HirFileId) -> bool {
        other == HirFileId::from(*self)
    }
}
impl PartialEq<EditionedFileId> for FileId {
    fn eq(&self, &other: &EditionedFileId) -> bool {
        *self == FileId::from(other)
    }
}
impl PartialEq<FileId> for EditionedFileId {
    fn eq(&self, &other: &FileId) -> bool {
        other == FileId::from(*self)
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroFileId {
    pub macro_call_id: MacroCallId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MacroCallId(InternId);
#[cfg(feature = "ra-salsa")]
impl ra_salsa::InternKey for MacroCallId {
    fn from_intern_id(v: ra_salsa::InternId) -> Self {
        MacroCallId(v)
    }
    fn as_intern_id(&self) -> ra_salsa::InternId {
        self.0
    }
}
impl MacroCallId {
    pub const MAX_ID: u32 = 0x7fff_ffff;
    pub fn as_file(self) -> HirFileId {
        MacroFileId { macro_call_id: self }.into()
    }
    pub fn as_macro_file(self) -> MacroFileId {
        MacroFileId { macro_call_id: self }
    }
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum HirFileIdRepr {
    FileId(EditionedFileId),
    MacroFile(MacroFileId),
}
impl fmt::Debug for HirFileIdRepr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::FileId(arg0) => arg0.fmt(f),
            Self::MacroFile(arg0) => {
                f.debug_tuple("MacroFile").field(&arg0.macro_call_id.0).finish()
            }
        }
    }
}
impl From<EditionedFileId> for HirFileId {
    #[allow(clippy::let_unit_value)]
    fn from(id: EditionedFileId) -> Self {
        assert!(id.as_u32() <= Self::MAX_HIR_FILE_ID, "FileId index {} is too large", id.as_u32());
        HirFileId(id.as_u32())
    }
}
impl From<MacroFileId> for HirFileId {
    #[allow(clippy::let_unit_value)]
    fn from(MacroFileId { macro_call_id: MacroCallId(id) }: MacroFileId) -> Self {
        let id = id.as_u32();
        assert!(id <= Self::MAX_HIR_FILE_ID, "MacroCallId index {id} is too large");
        HirFileId(id | Self::MACRO_FILE_TAG_MASK)
    }
}
impl HirFileId {
    const MAX_HIR_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
    const MACRO_FILE_TAG_MASK: u32 = 1 << 31;
    #[inline]
    pub fn is_macro(self) -> bool {
        self.0 & Self::MACRO_FILE_TAG_MASK != 0
    }
    #[inline]
    pub fn macro_file(self) -> Option<MacroFileId> {
        match self.0 & Self::MACRO_FILE_TAG_MASK {
            0 => None,
            _ => Some(MacroFileId {
                macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
            }),
        }
    }
    #[inline]
    pub fn file_id(self) -> Option<EditionedFileId> {
        match self.0 & Self::MACRO_FILE_TAG_MASK {
            0 => Some(EditionedFileId(self.0)),
            _ => None,
        }
    }
    #[inline]
    pub fn repr(self) -> HirFileIdRepr {
        match self.0 & Self::MACRO_FILE_TAG_MASK {
            0 => HirFileIdRepr::FileId(EditionedFileId(self.0)),
            _ => HirFileIdRepr::MacroFile(MacroFileId {
                macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
            }),
        }
    }
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct TokenId(pub u32);
impl std::fmt::Debug for TokenId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}
#[cfg(not(feature = "ra-salsa"))]
mod intern_id_proxy {
    use std::fmt;
    use std::num::NonZeroU32;
    #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
    pub(super) struct InternId {
        value: NonZeroU32,
    }
    impl InternId {
        pub(super) const MAX: u32 = 0xFFFF_FF00;
        pub(super) const unsafe fn new_unchecked(value: u32) -> Self {
            debug_assert!(value < InternId::MAX);
            let value = unsafe { NonZeroU32::new_unchecked(value + 1) };
            InternId { value }
        }
        pub(super) fn as_u32(self) -> u32 {
            self.value.get() - 1
        }
        pub(super) fn as_usize(self) -> usize {
            self.as_u32() as usize
        }
    }
    impl From<InternId> for u32 {
        fn from(raw: InternId) -> u32 {
            raw.as_u32()
        }
    }
    impl From<InternId> for usize {
        fn from(raw: InternId) -> usize {
            raw.as_usize()
        }
    }
    impl From<u32> for InternId {
        fn from(id: u32) -> InternId {
            assert!(id < InternId::MAX);
            unsafe { InternId::new_unchecked(id) }
        }
    }
    impl From<usize> for InternId {
        fn from(id: usize) -> InternId {
            assert!(id < (InternId::MAX as usize));
            unsafe { InternId::new_unchecked(id as u32) }
        }
    }
    impl fmt::Debug for InternId {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            self.as_usize().fmt(f)
        }
    }
    impl fmt::Display for InternId {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            self.as_usize().fmt(f)
        }
    }
}
#[cfg(not(feature = "ra-salsa"))]
use intern_id_proxy::InternId;