codive-lsp 0.1.0

LSP client infrastructure for Codive
Documentation
//! LSP types and re-exports
//!
//! This module provides type definitions used throughout the LSP integration,
//! re-exporting common types from `lsp-types` for convenience.

use serde::{Deserialize, Serialize};

// Re-export commonly used LSP types
pub use lsp_types::{
    CallHierarchyIncomingCall, CallHierarchyItem, CallHierarchyOutgoingCall, Diagnostic,
    DiagnosticSeverity, DocumentSymbol, DocumentSymbolResponse, GotoDefinitionResponse, Hover,
    HoverContents, Location, MarkedString, MarkupContent, MarkupKind, Position, Range,
    SymbolInformation, SymbolKind, TextDocumentIdentifier, TextDocumentPositionParams, Uri,
    WorkspaceSymbolResponse,
};
pub use url::Url;

/// Status of an LSP server connection
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LspStatus {
    /// Server ID (e.g., "rust-analyzer", "typescript")
    pub id: String,
    /// Human-readable name
    pub name: String,
    /// Project root this server is handling
    pub root: String,
    /// Connection status
    pub status: LspConnectionStatus,
}

/// Connection status for an LSP server
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LspConnectionStatus {
    /// Server is connected and ready
    Connected,
    /// Server failed to initialize
    Error,
    /// Server is starting up
    Starting,
}

/// Result from an LSP operation
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum LspResult {
    /// Definition/reference locations
    Locations(Vec<Location>),
    /// Hover information
    Hover(Option<Hover>),
    /// Document symbols (flat or hierarchical)
    DocumentSymbols(DocumentSymbolResponse),
    /// Workspace symbols
    WorkspaceSymbols(WorkspaceSymbolResponse),
    /// Call hierarchy items
    CallHierarchyItems(Vec<CallHierarchyItem>),
    /// Incoming calls
    IncomingCalls(Vec<CallHierarchyIncomingCall>),
    /// Outgoing calls
    OutgoingCalls(Vec<CallHierarchyOutgoingCall>),
    /// Diagnostics
    Diagnostics(Vec<Diagnostic>),
    /// Empty result
    Empty,
}

/// LSP operation types
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum LspOperation {
    /// textDocument/definition
    GoToDefinition,
    /// textDocument/references
    FindReferences,
    /// textDocument/hover
    Hover,
    /// textDocument/documentSymbol
    DocumentSymbol,
    /// workspace/symbol
    WorkspaceSymbol,
    /// textDocument/implementation
    GoToImplementation,
    /// textDocument/prepareCallHierarchy
    PrepareCallHierarchy,
    /// callHierarchy/incomingCalls
    IncomingCalls,
    /// callHierarchy/outgoingCalls
    OutgoingCalls,
}

impl std::fmt::Display for LspOperation {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            LspOperation::GoToDefinition => write!(f, "goToDefinition"),
            LspOperation::FindReferences => write!(f, "findReferences"),
            LspOperation::Hover => write!(f, "hover"),
            LspOperation::DocumentSymbol => write!(f, "documentSymbol"),
            LspOperation::WorkspaceSymbol => write!(f, "workspaceSymbol"),
            LspOperation::GoToImplementation => write!(f, "goToImplementation"),
            LspOperation::PrepareCallHierarchy => write!(f, "prepareCallHierarchy"),
            LspOperation::IncomingCalls => write!(f, "incomingCalls"),
            LspOperation::OutgoingCalls => write!(f, "outgoingCalls"),
        }
    }
}

impl std::str::FromStr for LspOperation {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "goToDefinition" => Ok(LspOperation::GoToDefinition),
            "findReferences" => Ok(LspOperation::FindReferences),
            "hover" => Ok(LspOperation::Hover),
            "documentSymbol" => Ok(LspOperation::DocumentSymbol),
            "workspaceSymbol" => Ok(LspOperation::WorkspaceSymbol),
            "goToImplementation" => Ok(LspOperation::GoToImplementation),
            "prepareCallHierarchy" => Ok(LspOperation::PrepareCallHierarchy),
            "incomingCalls" => Ok(LspOperation::IncomingCalls),
            "outgoingCalls" => Ok(LspOperation::OutgoingCalls),
            _ => Err(format!("Unknown LSP operation: {}", s)),
        }
    }
}

/// Symbol kinds that are typically most useful for navigation
pub const IMPORTANT_SYMBOL_KINDS: &[SymbolKind] = &[
    SymbolKind::CLASS,
    SymbolKind::FUNCTION,
    SymbolKind::METHOD,
    SymbolKind::INTERFACE,
    SymbolKind::VARIABLE,
    SymbolKind::CONSTANT,
    SymbolKind::STRUCT,
    SymbolKind::ENUM,
];

/// Format a diagnostic for display
pub fn format_diagnostic(diagnostic: &Diagnostic) -> String {
    let severity = match diagnostic.severity {
        Some(DiagnosticSeverity::ERROR) => "ERROR",
        Some(DiagnosticSeverity::WARNING) => "WARN",
        Some(DiagnosticSeverity::INFORMATION) => "INFO",
        Some(DiagnosticSeverity::HINT) => "HINT",
        None => "UNKNOWN",
        _ => "UNKNOWN",
    };

    let line = diagnostic.range.start.line + 1;
    let col = diagnostic.range.start.character + 1;

    format!("{} [{}:{}] {}", severity, line, col, diagnostic.message)
}