lsp-mcp 0.1.0

MCP server providing unified access to Language Server Protocol features
Documentation
use crate::error::Result;
use crate::lsp::LanguageServerManager;
use crate::tools::{ensure_document_open, make_position};
use lsp_types::{Hover, HoverContents, MarkedString, MarkupContent};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

/// Result of hover operation
#[derive(Debug, Serialize, Deserialize)]
pub struct HoverResult {
    pub contents: String,
    pub range: Option<RangeInfo>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RangeInfo {
    pub start_line: u32,
    pub start_character: u32,
    pub end_line: u32,
    pub end_character: u32,
}

/// Get hover information at a position
pub fn hover(
    manager: &LanguageServerManager,
    file_path: &str,
    line: u32,
    character: u32,
) -> Result<Option<HoverResult>> {
    let path = PathBuf::from(file_path);
    let (client, uri) = ensure_document_open(manager, &path)?;

    let position = make_position(line, character);
    let hover = client.hover(&uri, position)?;

    Ok(hover.map(|h| convert_hover(h)))
}

fn convert_hover(hover: Hover) -> HoverResult {
    let contents = extract_hover_contents(&hover.contents);
    let range = hover.range.map(|r| RangeInfo {
        start_line: r.start.line,
        start_character: r.start.character,
        end_line: r.end.line,
        end_character: r.end.character,
    });

    HoverResult { contents, range }
}

fn extract_hover_contents(contents: &HoverContents) -> String {
    match contents {
        HoverContents::Scalar(marked) => extract_marked_string(marked),
        HoverContents::Array(arr) => arr
            .iter()
            .map(extract_marked_string)
            .collect::<Vec<_>>()
            .join("\n\n"),
        HoverContents::Markup(markup) => extract_markup_content(markup),
    }
}

fn extract_marked_string(marked: &MarkedString) -> String {
    match marked {
        MarkedString::String(s) => s.clone(),
        MarkedString::LanguageString(ls) => {
            format!("```{}\n{}\n```", ls.language, ls.value)
        }
    }
}

fn extract_markup_content(markup: &MarkupContent) -> String {
    markup.value.clone()
}