mcp-cpp-server 0.2.2

A high-performance Model Context Protocol (MCP) server for C++ code analysis using clangd LSP integration
//! Definition and declaration analysis functionality for C++ symbols
//!
//! This module provides LSP-based definition and declaration analysis capabilities
//! that work with clangd to find where symbols are defined and declared, supporting
//! multiple locations and various LSP response formats.

use crate::clangd::session::ClangdSessionTrait;
use crate::lsp::traits::LspClientTrait;
use crate::mcp_server::tools::analyze_symbols::AnalyzerError;
use crate::project::component_session::ComponentSession;
use crate::symbol::FileLocation;
use tracing::trace;

// ============================================================================
// Public API
// ============================================================================

/// Get the declaration locations of a symbol
pub async fn get_declarations(
    symbol_location: &FileLocation,
    component_session: &ComponentSession,
) -> Result<Vec<FileLocation>, AnalyzerError> {
    let uri = symbol_location.get_uri();
    let lsp_position: lsp_types::Position = symbol_location.range.start.into();

    // Ensure file is ready first
    component_session
        .ensure_file_ready(&symbol_location.file_path)
        .await?;

    // Get LSP session and make the request
    let mut session = component_session.lsp_session().await;
    let declaration = session
        .client_mut()
        .text_document_declaration(uri, lsp_position)
        .await
        .map_err(AnalyzerError::from)?;

    goto_defdecl_response_to_file_locations(declaration)
}

/// Get the definition locations of a symbol
pub async fn get_definitions(
    symbol_location: &FileLocation,
    component_session: &ComponentSession,
) -> Result<Vec<FileLocation>, AnalyzerError> {
    let uri = symbol_location.get_uri();
    let lsp_position: lsp_types::Position = symbol_location.range.start.into();

    // Ensure file is ready first
    component_session
        .ensure_file_ready(&symbol_location.file_path)
        .await?;

    // Get LSP session and make the request
    let mut session = component_session.lsp_session().await;
    let definition = session
        .client_mut()
        .text_document_definition(uri, lsp_position)
        .await
        .map_err(AnalyzerError::from)?;

    goto_defdecl_response_to_file_locations(definition)
}

// ============================================================================
// Response Processing Utilities
// ============================================================================

/// Convert LSP GotoDefinitionResponse to FileLocation vector
pub fn goto_defdecl_response_to_file_locations(
    response: lsp_types::GotoDefinitionResponse,
) -> Result<Vec<FileLocation>, AnalyzerError> {
    use lsp_types::GotoDefinitionResponse;
    trace!("Parsing GotoDefinitionReponse: {:?}", response);
    match response {
        GotoDefinitionResponse::Scalar(loc) => Ok(vec![FileLocation::from(&loc)]),
        GotoDefinitionResponse::Array(locs) => Ok(locs.iter().map(FileLocation::from).collect()),
        GotoDefinitionResponse::Link(links) => Ok(links.iter().map(FileLocation::from).collect()),
    }
}