use serde::{Deserialize, Serialize};
use tracing::{debug, info};
use crate::mcp_server::tools::analyze_symbols::AnalyzerError;
use crate::mcp_server::tools::lsp_helpers::document_symbols::{
extract_class_members, get_document_symbols,
};
use crate::project::component_session::ComponentSession;
use crate::symbol::FileLocation;
#[derive(Debug, Serialize, Deserialize)]
pub struct Member {
pub name: String,
pub member_type: String,
pub signature: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub access: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Members {
pub methods: Vec<Member>,
pub constructors: Vec<Member>,
pub destructors: Vec<Member>,
pub operators: Vec<Member>,
}
#[allow(dead_code)]
pub async fn get_members(
symbol_location: &FileLocation,
component_session: &ComponentSession,
target_name: &str,
) -> Result<Members, AnalyzerError> {
let uri = symbol_location.get_uri();
let document_symbols = get_document_symbols(component_session, uri).await?;
info!(
"Analyzing {} document symbols for class '{}' members",
document_symbols.len(),
target_name
);
let member_symbols = extract_class_members(&document_symbols, target_name);
debug!(
"Found {} callable members for class '{}'",
member_symbols.len(),
target_name
);
let members = categorize_members_from_symbols(member_symbols, target_name);
Ok(members)
}
#[allow(dead_code)]
pub fn get_members_from_symbols(
document_symbols: &[lsp_types::DocumentSymbol],
target_name: &str,
) -> Members {
info!(
"Analyzing {} document symbols for class '{}' members",
document_symbols.len(),
target_name
);
let member_symbols = extract_class_members(document_symbols, target_name);
debug!(
"Found {} callable members for class '{}'",
member_symbols.len(),
target_name
);
categorize_members_from_symbols(member_symbols, target_name)
}
pub fn get_members_from_document_symbol(
document_symbol: &lsp_types::DocumentSymbol,
target_name: &str,
) -> Members {
debug!(
"Extracting members directly from document symbol for class '{}'",
target_name
);
let member_symbols: Vec<&lsp_types::DocumentSymbol> = document_symbol
.children
.as_ref()
.map(|children| children.iter().collect())
.unwrap_or_default();
debug!(
"Found {} direct members for class '{}'",
member_symbols.len(),
target_name
);
categorize_members_from_symbols(member_symbols, target_name)
}
fn categorize_members_from_symbols(
member_symbols: Vec<&lsp_types::DocumentSymbol>,
target_name: &str,
) -> Members {
let mut methods = Vec::new();
let mut constructors = Vec::new();
let mut destructors = Vec::new();
let mut operators = Vec::new();
debug!(
"Categorizing {} member symbols for class '{}'",
member_symbols.len(),
target_name
);
for symbol in member_symbols {
debug!(
"Processing member: '{}' (kind: {:?}, detail: {:?})",
symbol.name, symbol.kind, symbol.detail
);
let member = create_member_from_document_symbol(symbol);
categorize_member(
member,
&mut methods,
&mut constructors,
&mut destructors,
&mut operators,
);
}
debug!(
"Member categorization complete for '{}': {} methods, {} constructors, {} destructors, {} operators",
target_name,
methods.len(),
constructors.len(),
destructors.len(),
operators.len()
);
Members {
methods,
constructors,
destructors,
operators,
}
}
fn create_member_from_document_symbol(symbol: &lsp_types::DocumentSymbol) -> Member {
let member_type = classify_member_kind(&symbol.name, symbol.kind);
Member {
name: symbol.name.clone(),
member_type,
signature: symbol.detail.clone().unwrap_or_else(|| symbol.name.clone()),
access: None, }
}
fn classify_member_kind(name: &str, symbol_kind: lsp_types::SymbolKind) -> String {
use lsp_types::SymbolKind;
match symbol_kind {
SymbolKind::CONSTRUCTOR => {
if name.starts_with("~") {
"destructor".to_string()
} else {
"constructor".to_string()
}
}
SymbolKind::METHOD => {
if name.starts_with("operator") {
"operator".to_string()
} else {
"method".to_string()
}
}
SymbolKind::FUNCTION => "method".to_string(), _ => "method".to_string(), }
}
fn categorize_member(
member: Member,
methods: &mut Vec<Member>,
constructors: &mut Vec<Member>,
destructors: &mut Vec<Member>,
operators: &mut Vec<Member>,
) {
match member.member_type.as_str() {
"method" => methods.push(member),
"constructor" => constructors.push(member),
"destructor" => destructors.push(member),
"operator" => operators.push(member),
_ => methods.push(member), }
}