mod decl_feature;
#[allow(clippy::module_inception)]
mod property;
use hashbrown::{HashMap, HashSet};
pub use decl_feature::{DeclFeatureFlag, PropertyDeclFeature};
use emmylua_parser::{LuaAstNode, LuaDocTagField, LuaDocType, LuaVersionCondition, VisibilityKind};
pub use property::LuaCommonProperty;
pub use property::{LuaDeprecated, LuaExport, LuaExportScope, LuaPropertyId};
pub use crate::db_index::property::property::LuaAttributeUse;
use crate::{DbIndex, FileId, LuaMember, LuaSignatureId};
use super::{LuaSemanticDeclId, traits::LuaIndex};
#[derive(Debug)]
pub struct LuaPropertyIndex {
properties: HashMap<LuaPropertyId, LuaCommonProperty>,
property_owners_map: HashMap<LuaSemanticDeclId, LuaPropertyId>,
id_count: u32,
in_filed_owner: HashMap<FileId, HashSet<LuaSemanticDeclId>>,
}
impl Default for LuaPropertyIndex {
fn default() -> Self {
Self::new()
}
}
impl LuaPropertyIndex {
pub fn new() -> Self {
Self {
id_count: 0,
in_filed_owner: HashMap::new(),
properties: HashMap::new(),
property_owners_map: HashMap::new(),
}
}
fn get_or_create_property(
&mut self,
owner_id: LuaSemanticDeclId,
) -> Option<(&mut LuaCommonProperty, LuaPropertyId)> {
if let Some(property_id) = self.property_owners_map.get(&owner_id) {
self.properties
.get_mut(property_id)
.map(|prop| (prop, *property_id))
} else {
let id = LuaPropertyId::new(self.id_count);
self.id_count += 1;
self.property_owners_map.insert(owner_id.clone(), id);
self.properties.insert(id, LuaCommonProperty::new());
self.properties.get_mut(&id).map(|prop| (prop, id))
}
}
pub fn add_owner_map(
&mut self,
source_owner_id: LuaSemanticDeclId,
same_property_owner_id: LuaSemanticDeclId,
file_id: FileId,
) -> Option<()> {
let (_, property_id) = self.get_or_create_property(source_owner_id.clone())?;
self.property_owners_map
.insert(same_property_owner_id, property_id);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(source_owner_id);
Some(())
}
pub fn add_description(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
description: String,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
property.add_extra_description(description);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn add_visibility(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
visibility: VisibilityKind,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
property.visibility = visibility;
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn add_source(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
source: String,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
property.add_extra_source(source);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn add_deprecated(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
message: Option<String>,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
property.add_extra_deprecated(message);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn add_version(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
version_conds: Vec<LuaVersionCondition>,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
property.add_extra_version_cond(version_conds);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn add_see(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
mut see_content: String,
see_description: Option<String>,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
if let Some(see_description) = see_description {
see_content += " ";
see_content += &see_description;
}
property.add_extra_tag("see".into(), see_content);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn add_other(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
tag_name: String,
other_content: String,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
property.add_extra_tag(tag_name, other_content);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn add_export(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
export: property::LuaExport,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
property.add_extra_export(export);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn add_decl_feature(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
feature: PropertyDeclFeature,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
property.add_decl_feature(feature);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn add_attribute_use(
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
attribute_use: LuaAttributeUse,
) -> Option<()> {
let (property, _) = self.get_or_create_property(owner_id.clone())?;
property.add_attribute_use(attribute_use);
self.in_filed_owner
.entry(file_id)
.or_default()
.insert(owner_id);
Some(())
}
pub fn get_property(&self, owner_id: &LuaSemanticDeclId) -> Option<&LuaCommonProperty> {
self.property_owners_map
.get(owner_id)
.and_then(|id| self.properties.get(id))
}
}
impl LuaIndex for LuaPropertyIndex {
fn remove(&mut self, file_id: FileId) {
if let Some(property_owner_ids) = self.in_filed_owner.remove(&file_id) {
for property_owner_id in property_owner_ids {
if let Some(property_id) = self.property_owners_map.remove(&property_owner_id) {
self.properties.remove(&property_id);
}
}
}
}
fn clear(&mut self) {
self.properties.clear();
self.property_owners_map.clear();
self.in_filed_owner.clear();
self.id_count = 0;
}
}
pub fn try_extract_signature_id_from_field(
db: &DbIndex,
member: &LuaMember,
) -> Option<LuaSignatureId> {
if !member.is_field() {
return None;
}
let root = db
.get_vfs()
.get_syntax_tree(&member.get_file_id())?
.get_red_root();
let field_node = member.get_syntax_id().to_node_from_root(&root)?;
let field_tag = LuaDocTagField::cast(field_node)?;
let type_node = field_tag.get_type()?;
match &type_node {
LuaDocType::Func(doc_func) => Some(LuaSignatureId::from_doc_func(
member.get_file_id(),
doc_func,
)),
_ => None,
}
}