cairo-language-server 2.20.0

The Cairo Language Server
Documentation
use itertools::Itertools;
use lsp_types::{
    ClientCapabilities, InlayHintClientCapabilities, InlayHintLabel, InlayHintLabelPartTooltip,
    InlayHintParams, MarkupContent, TextDocumentClientCapabilities, lsp_request,
};

use crate::support::cairo_project_toml::CAIRO_PROJECT_TOML_2025_12;
use crate::support::cursor::index_in_text;
use crate::support::{cursors, sandbox};

mod params;
mod variables;

fn inlay_hint(cairo_code: &str) -> String {
    let (mut cairo, cursors) = cursors(cairo_code);

    let mut ls = sandbox! {
        files {
            "cairo_project.toml" => CAIRO_PROJECT_TOML_2025_12,
            "src/lib.cairo" => cairo.clone()
        }
        client_capabilities = caps;
    };

    ls.open_and_wait_for_diagnostics("src/lib.cairo");

    let inlay_hint_params = InlayHintParams {
        text_document: ls.doc_id("src/lib.cairo"),
        range: cursors.assert_single_selection(),
        work_done_progress_params: Default::default(),
    };
    let inlay_hints = ls.send_request::<lsp_request!("textDocument/inlayHint")>(inlay_hint_params);

    let Some(mut inlay_hints) = inlay_hints else {
        panic!("Inlay hint request failed.");
    };

    inlay_hints.sort_by_key(|a| a.position);
    inlay_hints.reverse();

    for hint in inlay_hints {
        let offset = index_in_text(&cairo, hint.position);

        let label = match hint.label {
            InlayHintLabel::String(label) => label,
            InlayHintLabel::LabelParts(parts) => parts
                .into_iter()
                .map(|part| {
                    let tooltip = part
                        .tooltip
                        .map(|tooltip| match tooltip {
                            InlayHintLabelPartTooltip::String(tooltip) => tooltip,
                            InlayHintLabelPartTooltip::MarkupContent(MarkupContent {
                                value,
                                ..
                            }) => value,
                        })
                        .map(|tooltip| format!(" tooltip={tooltip:?}"))
                        .unwrap_or_default();

                    format!("<hint{tooltip}>{}</hint>", part.value)
                })
                .join(""),
        };

        cairo.insert_str(offset, &label);
    }

    cairo
}

fn caps(base: ClientCapabilities) -> ClientCapabilities {
    ClientCapabilities {
        text_document: base.text_document.or_else(Default::default).map(|it| {
            TextDocumentClientCapabilities {
                inlay_hint: Some(InlayHintClientCapabilities {
                    dynamic_registration: Some(false),
                    ..Default::default()
                }),
                ..it
            }
        }),
        ..base
    }
}