use std::{hash::BuildHasherDefault, num::NonZeroUsize};
use indexmap::IndexSet;
use rustc_hash::{FxHashMap, FxHasher};
use swc_common::SyntaxContext;
use swc_ecma_ast::{Id, Ident};
use super::{TsBindings, TsContainerId, TsNamespaceBodyId};
#[doc(hidden)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TsLexicalNamespaceId(NonZeroUsize);
#[doc(hidden)]
#[derive(Default)]
pub struct TsLexicalScopes {
namespaces: IndexSet<Id, BuildHasherDefault<FxHasher>>,
owners: FxHashMap<SyntaxContext, TsLexicalNamespaceId>,
}
impl TsLexicalScopes {
pub fn clear(&mut self) {
self.namespaces.clear();
self.owners.clear();
}
pub fn namespace(&mut self, ident: &Ident) -> (TsLexicalNamespaceId, bool) {
let (index, inserted) = self.namespaces.insert_full(ident.to_id());
let id = NonZeroUsize::new(index + 1).expect("namespace index must fit in usize");
(TsLexicalNamespaceId(id), inserted)
}
pub fn scope(&mut self, ctxt: SyntaxContext, owner: TsLexicalNamespaceId) {
self.owners.insert(ctxt, owner);
}
pub fn has_namespaces(&self) -> bool {
!self.namespaces.is_empty()
}
pub(super) fn resolve(self, bindings: &TsBindings) -> ScopeOwners {
if self.owners.is_empty() {
return ScopeOwners::default();
}
let containers = self
.namespaces
.iter()
.map(|id| bindings.container(id))
.collect();
ScopeOwners::Lexical {
owners: self.owners,
containers,
}
}
}
#[derive(Debug)]
pub(super) enum ScopeOwners {
Bodies(FxHashMap<SyntaxContext, TsNamespaceBodyId>),
Lexical {
owners: FxHashMap<SyntaxContext, TsLexicalNamespaceId>,
containers: Vec<Option<TsContainerId>>,
},
}
impl Default for ScopeOwners {
fn default() -> Self {
Self::Bodies(FxHashMap::default())
}
}
impl ScopeOwners {
pub(super) fn insert(&mut self, ctxt: SyntaxContext, body: TsNamespaceBodyId) {
let Self::Bodies(owners) = self else {
unreachable!("lexical scope owners are attached after declaration collection");
};
owners.insert(ctxt, body);
}
pub(super) fn get(&self, ctxt: SyntaxContext) -> Option<ScopeOwner> {
match self {
Self::Bodies(owners) => owners.get(&ctxt).copied().map(ScopeOwner::Body),
Self::Lexical { owners, containers } => owners
.get(&ctxt)
.and_then(|owner| containers[owner.0.get() - 1])
.map(ScopeOwner::Container),
}
}
}
#[derive(Clone, Copy)]
pub(super) enum ScopeOwner {
Body(TsNamespaceBodyId),
Container(TsContainerId),
}
impl ScopeOwner {
pub(super) fn matches(self, body: TsNamespaceBodyId, container: TsContainerId) -> bool {
match self {
Self::Body(owner) => owner == body,
Self::Container(owner) => owner == container,
}
}
}