omena-lsp-server 0.3.0

Rust LSP server boundary scaffold for Omena CSS Modules
Documentation
use omena_query::{
    OmenaQueryExplainInputV0, OmenaQueryExplainSymbolKindV0, OmenaQueryTransformExecutionContextV0,
    execute_omena_query_transform_passes_from_source, explain_omena_query,
    explain_omena_query_tree_shake_for_style_source,
    resolve_omena_query_source_precision_for_source,
    summarize_omena_query_style_diagnostics_for_file,
};

use super::*;

#[test]
fn shared_explain_request_preserves_query_response_structure() -> TestResult {
    let style_uri = "file:///workspace-a/src/App.module.scss";
    let style_text = "@import 'legacy';\n.button { color: red; }\n";
    let tree_style_uri = "file:///workspace-a/src/Tree.module.css";
    let tree_style_text = ".button { color: red; }\n";
    let source_uri = "file:///workspace-a/src/App.ts";
    let source_text = "const className = 'button';\nclassName;";
    let mut state = LspShellState::default();
    open_document(&mut state, style_uri, "scss", style_text);
    open_document(&mut state, tree_style_uri, "css", tree_style_text);
    open_document(&mut state, source_uri, "typescript", source_text);

    let diagnostic = summarize_omena_query_style_diagnostics_for_file(style_uri, style_text, &[])
        .diagnostics
        .into_iter()
        .find(|diagnostic| diagnostic.code == "deprecatedSassImport")
        .ok_or("fixture should produce the requested diagnostic")?;
    let diagnostic_direct = explain_omena_query(OmenaQueryExplainInputV0::Diagnostic {
        style_path: style_uri,
        diagnostic: &diagnostic,
    });
    assert_explain_result_equals(
        &mut state,
        json!({
            "kind": "diagnostic",
            "textDocument": { "uri": style_uri },
            "code": "deprecatedSassImport",
        }),
        &diagnostic_direct,
    );

    let execution = execute_omena_query_transform_passes_from_source(
        style_uri,
        style_text,
        &["print-css".to_string()],
    );
    let transform_direct = explain_omena_query(OmenaQueryExplainInputV0::Transform {
        decision: &execution.execution.decisions[0],
        decision_ordinal: 0,
    });
    assert_explain_result_equals(
        &mut state,
        json!({
            "kind": "transform",
            "textDocument": { "uri": style_uri },
            "passId": "print-css",
        }),
        &transform_direct,
    );

    let context: OmenaQueryTransformExecutionContextV0 = serde_json::from_value(json!({
        "reachableClassNames": ["button"],
    }))?;
    let tree_shake_direct = explain_omena_query_tree_shake_for_style_source(
        tree_style_uri,
        tree_style_text,
        &context,
        OmenaQueryExplainSymbolKindV0::Class,
        "button",
    )
    .ok_or("fixture should produce a closed-world bundle")?;
    assert_explain_result_equals(
        &mut state,
        json!({
            "kind": "treeShake",
            "textDocument": { "uri": tree_style_uri },
            "symbolKind": "class",
            "symbolName": "button",
            "context": { "reachableClassNames": ["button"] },
        }),
        &tree_shake_direct,
    );

    let reference_offset = source_text.rfind("className").ok_or("missing reference")?;
    let precision_reference = resolve_omena_query_source_precision_for_source(
        source_uri,
        source_text,
        Some("typescript"),
        "className",
        reference_offset,
    );
    let precision_direct = explain_omena_query(OmenaQueryExplainInputV0::Precision {
        reference: &precision_reference,
    });
    assert_explain_result_equals(
        &mut state,
        json!({
            "kind": "precision",
            "textDocument": { "uri": source_uri },
            "position": parser_position_for_byte_offset(source_text, reference_offset),
            "variableName": "className",
        }),
        &precision_direct,
    );

    let style_graph = explain_request(
        &mut state,
        json!({
            "kind": "styleGraph",
            "textDocument": { "uri": style_uri },
        }),
    );
    assert_eq!(
        style_graph["product"],
        json!("omena-lsp-server.style-graph")
    );
    assert_eq!(style_graph["availability"], json!("available"));
    assert_ne!(style_graph["graph"], Value::Null);
    Ok(())
}

#[test]
fn hover_trace_keeps_its_wire_shape_while_using_the_shared_egress() {
    let before = crate::explain::hover_egress_projection_count();
    let mut state = LspShellState::default();
    let response = handle_lsp_message(
        &mut state,
        json!({
            "jsonrpc": "2.0",
            "id": 1,
            "method": EXPLAIN_HOVER_TRACE_REQUEST,
            "params": {
                "textDocument": { "uri": "file:///workspace-a/src/App.module.scss" },
            },
        }),
    );
    assert_eq!(
        response.and_then(|value| value.get("result").cloned()),
        Some(json!({
            "schemaVersion": "0",
            "product": "omena-lsp-server.explain-hover-trace",
            "documentUri": "file:///workspace-a/src/App.module.scss",
            "workspaceFolderUri": null,
            "fileKind": "unknown",
            "queryPosition": null,
            "matched": false,
            "reason": "missingPosition",
            "candidateCount": 0,
            "definitionCount": 0,
            "candidates": [],
            "definitions": [],
            "resolutionPath": [],
            "readySurfaces": ["explainHoverTraceRpc"],
        })),
    );
    assert_eq!(crate::explain::hover_egress_projection_count(), before + 1);
}

fn assert_explain_result_equals<T: serde::Serialize>(
    state: &mut LspShellState,
    params: Value,
    expected: &T,
) {
    assert_eq!(explain_request(state, params), json!(expected));
}

fn explain_request(state: &mut LspShellState, params: Value) -> Value {
    handle_lsp_message(
        state,
        json!({
            "jsonrpc": "2.0",
            "id": 1,
            "method": EXPLAIN_REQUEST,
            "params": params,
        }),
    )
    .and_then(|value| value.get("result").cloned())
    .unwrap_or(Value::Null)
}

fn open_document(state: &mut LspShellState, uri: &str, language_id: &str, text: &str) {
    handle_lsp_message(
        state,
        json!({
            "jsonrpc": "2.0",
            "method": "textDocument/didOpen",
            "params": {
                "textDocument": {
                    "uri": uri,
                    "languageId": language_id,
                    "version": 1,
                    "text": text,
                },
            },
        }),
    );
}