use crate::models::{EntityKind, ParsedEntity, ReferenceIntent};
use crate::pipeline::parser::comments::{extract_comments, extract_decorators};
use crate::pipeline::parser::context::{ClassContext, compute_fqn_and_context};
use crate::pipeline::parser::languages::{
cpp, java, javascript, kotlin, markdown, python, typescript,
};
use crate::pipeline::parser::utils::{extract_decorator_references, extract_type_references};
use super::captures::CaptureState;
#[expect(
clippy::too_many_arguments,
reason = "entity enrichment requires state, source, language, and callbacks from extraction context"
)]
#[expect(
clippy::too_many_lines,
reason = "function is verbose but correct — extraction deferred"
)]
#[expect(
clippy::cognitive_complexity,
reason = "function is verbose but correct — extraction deferred"
)]
pub(crate) fn enrich_and_create_entity<'a>(
state: &mut CaptureState<'a>,
source_bytes: &[u8],
lang_name: &str,
file_path: &str,
repo_name: &str,
class_contexts: &[ClassContext],
java_package: &Option<String>,
entities: &mut Vec<ParsedEntity>,
covered_ranges: &mut Vec<(usize, usize)>,
) {
let name = state.name.take();
let kind = state.kind.take();
let mut signature = state.signature.take();
let start_line = state.start_line;
let end_line;
let entity_node = state.entity_node.take();
let mut reference_intents = std::mem::take(&mut state.reference_intents);
if let (Some(mut name), Some(kind)) = (name, kind) {
let (docstring, inline_comments) = if let Some(node) = entity_node {
extract_comments(node, source_bytes, lang_name, &kind, class_contexts)
} else {
(None, Vec::new())
};
let mut decorators = if let Some(node) = entity_node {
extract_decorators(node, source_bytes, lang_name)
} else {
Vec::new()
};
if lang_name == "python"
&& let Some(entity_n) = entity_node
{
python::extract_decorator_names_python(entity_n, source_bytes, &mut decorators);
}
let (mut fqn, mut enclosing_class) =
compute_fqn_and_context(&name, &kind, start_line, lang_name, class_contexts);
if let Some(pkg) = java_package {
fqn = format!("{}.{}", pkg, fqn);
}
if matches!(
kind,
EntityKind::CppMethod
| EntityKind::CFunction
| EntityKind::CppClass
| EntityKind::CStruct
| EntityKind::CppNamespace
| EntityKind::MacroDefinition
) && let Some(node) = entity_node
&& let Some(cpp_fqn) = cpp::build_cpp_fqn(node, source_bytes)
&& !cpp_fqn.is_empty()
{
enclosing_class = Some(cpp_fqn.clone());
fqn = format!("{}::{}", cpp_fqn, name);
}
if lang_name == "markdown" {
if matches!(kind, EntityKind::MarkdownDocument) {
name = format!("{}::{}", repo_name, file_path);
}
fqn = match kind {
EntityKind::MarkdownDocument => file_path.to_string(),
EntityKind::MarkdownSection => {
if let Some(node) = entity_node {
let chain = markdown::build_markdown_fqn(node, source_bytes);
format!("{}::{}", file_path, chain)
} else {
format!("{}::{}", file_path, name)
}
}
_ => fqn,
};
}
if matches!(
kind,
EntityKind::Class
| EntityKind::Interface
| EntityKind::KotlinClass
| EntityKind::KotlinInterface
| EntityKind::KotlinObject
| EntityKind::KotlinEnum
) && let Some(class_node) = entity_node
{
if lang_name == "javascript" {
javascript::extract_class_inheritance_js(
class_node,
source_bytes,
&mut reference_intents,
);
let decorator_node = class_node
.parent()
.filter(|p| p.kind() == "export_statement")
.unwrap_or(class_node);
extract_decorator_references(decorator_node, source_bytes, &mut reference_intents);
} else if lang_name == "typescript" {
typescript::extract_class_inheritance(
class_node,
source_bytes,
&mut reference_intents,
);
let decorator_node = class_node
.parent()
.filter(|p| p.kind() == "export_statement")
.unwrap_or(class_node);
extract_decorator_references(decorator_node, source_bytes, &mut reference_intents);
extract_type_references(class_node, source_bytes, &mut reference_intents);
} else if lang_name == "java" {
java::extract_class_inheritance_java(
class_node,
source_bytes,
&mut reference_intents,
);
java::extract_annotation_references(
class_node,
source_bytes,
&mut reference_intents,
);
extract_type_references(class_node, source_bytes, &mut reference_intents);
} else if lang_name == "kotlin" {
kotlin::extract_class_inheritance_kotlin(
class_node,
source_bytes,
&mut reference_intents,
);
kotlin::extract_annotation_references(
class_node,
source_bytes,
&mut reference_intents,
);
kotlin::extract_type_references(class_node, source_bytes, &mut reference_intents);
}
}
if let Some(node) = entity_node {
end_line = node.end_position().row + 1;
} else {
end_line = start_line;
}
if signature.is_none()
&& matches!(kind, EntityKind::CppMethod | EntityKind::CFunction)
&& let Some(node) = entity_node
{
signature = cpp::extract_cpp_signature(node, source_bytes);
}
let mut entity = ParsedEntity::new(
name,
kind,
fqn,
signature,
docstring,
lang_name,
file_path,
start_line,
end_line,
enclosing_class,
repo_name,
);
entity.reference_intents = reference_intents;
entity.reference_intents.retain(|intent| match intent {
ReferenceIntent::TypeReference { type_name, .. } => type_name != entity.name.as_str(),
ReferenceIntent::ValueReference { value_name, .. } => {
value_name != entity.name.as_str()
}
_ => true,
});
entity.inline_comments = inline_comments;
entity.decorators = decorators;
if lang_name == "markdown"
&& let Some(node) = entity_node
{
let body = match entity.kind {
EntityKind::MarkdownDocument => {
markdown::extract_document_intro(node, source_bytes)
}
EntityKind::MarkdownSection => {
node.utf8_text(source_bytes).unwrap_or("").to_string()
}
_ => String::new(),
};
let header = format!("[{}] {}", entity.kind, entity.name);
let location = format!("File: {}:{}", entity.file_path, entity.start_line);
entity.embed_text = format!("{header}\n{location}\n\n{body}");
}
if lang_name == "javascript"
&& entity.kind == EntityKind::Constant
&& let Some(node) = entity_node
{
entity.alias_module_path = javascript::extract_require_module_path(node, source_bytes);
}
if let Some(node) = entity_node {
covered_ranges.push((node.start_byte(), node.end_byte()));
} else {
covered_ranges.push((usize::MAX, usize::MAX));
}
entities.push(entity);
}
}