omena-lsp-server 0.2.0

Rust LSP server boundary scaffold for Omena CSS Modules
Documentation
use super::*;

#[test]
fn resolves_classnames_bind_dynamic_source_expressions() -> TestResult {
    let source_text = r#"import bind from "classnames/bind";
import styles from "./styles.module.scss";
const cx = bind.bind(styles);
const tone = "item--primary";
const icon = { glyph: "item__icon" };
const prefix = "item--";
export const view = <div className={cx(tone, icon.glyph, `item--${variant}`, { "item--danger": danger, item__label: true }, ok && "item--ok", active ? "item--on" : "item--off", prefix + state)} />;
"#;
    let mut state = LspShellState::default();
    handle_lsp_message(
        &mut state,
        json!({
            "jsonrpc": "2.0",
            "id": 1,
            "method": "initialize",
            "params": {
                "workspaceFolders": [
                    {
                        "uri": "file:///workspace-a",
                        "name": "workspace-a",
                    },
                ],
            },
        }),
    );
    handle_lsp_message(
        &mut state,
        json!({
            "jsonrpc": "2.0",
            "method": "textDocument/didOpen",
            "params": {
                "textDocument": {
                    "uri": "file:///workspace-a/src/App.tsx",
                    "languageId": "typescriptreact",
                    "version": 1,
                    "text": source_text,
                },
            },
        }),
    );
    handle_lsp_message(
        &mut state,
        json!({
            "jsonrpc": "2.0",
            "method": "textDocument/didOpen",
            "params": {
                "textDocument": {
                    "uri": "file:///workspace-a/src/styles.module.scss",
                    "languageId": "scss",
                    "version": 1,
                    "text": ".item--primary {}\n.item__icon {}\n.item--large {}\n.item--danger {}\n.item__label {}\n.item--ok {}\n.item--on {}\n.item--off {}\n.item--muted {}\n",
                },
            },
        }),
    );

    let tone_position = parser_position_for_byte_offset(
        source_text,
        fixture_find(
            source_text,
            "tone,",
            "source fixture contains tone reference",
        )?,
    );
    let tone_definition = handle_lsp_message(
        &mut state,
        json!({
            "jsonrpc": "2.0",
            "id": 2,
            "method": "textDocument/definition",
            "params": {
                "textDocument": {
                    "uri": "file:///workspace-a/src/App.tsx",
                },
                "position": tone_position,
            },
        }),
    );
    assert_eq!(
        tone_definition
            .as_ref()
            .and_then(|value| value.pointer("/result/0/range")),
        Some(&json!({
            "start": {
                "line": 0,
                "character": 1,
            },
            "end": {
                "line": 0,
                "character": 14,
            },
        })),
    );

    let icon_position = parser_position_for_byte_offset(
        source_text,
        fixture_find(
            source_text,
            "icon.glyph",
            "source fixture contains object property reference",
        )?,
    );
    let icon_definition = handle_lsp_message(
        &mut state,
        json!({
            "jsonrpc": "2.0",
            "id": 3,
            "method": "textDocument/definition",
            "params": {
                "textDocument": {
                    "uri": "file:///workspace-a/src/App.tsx",
                },
                "position": icon_position,
            },
        }),
    );
    assert_eq!(
        icon_definition
            .as_ref()
            .and_then(|value| value.pointer("/result/0/range")),
        Some(&json!({
            "start": {
                "line": 1,
                "character": 1,
            },
            "end": {
                "line": 1,
                "character": 11,
            },
        })),
    );

    let template_position = parser_position_for_byte_offset(
        source_text,
        fixture_find(
            source_text,
            "`item--",
            "source fixture contains template prefix reference",
        )? + 1,
    );
    let template_definition = handle_lsp_message(
        &mut state,
        json!({
            "jsonrpc": "2.0",
            "id": 4,
            "method": "textDocument/definition",
            "params": {
                "textDocument": {
                    "uri": "file:///workspace-a/src/App.tsx",
                },
                "position": template_position,
            },
        }),
    );
    let template_targets = template_definition
        .as_ref()
        .and_then(|value| value.pointer("/result"))
        .and_then(Value::as_array)
        .cloned()
        .unwrap_or_default();
    assert!(
        template_targets
            .iter()
            .any(|target| target.pointer("/range/start/line") == Some(&json!(2)))
    );
    assert!(
        !template_targets
            .iter()
            .any(|target| target.pointer("/range/start/line") == Some(&json!(1)))
    );

    let object_key_position = parser_position_for_byte_offset(
        source_text,
        fixture_find(
            source_text,
            "item__label",
            "source fixture contains object key reference",
        )?,
    );
    let object_key_definition = handle_lsp_message(
        &mut state,
        json!({
            "jsonrpc": "2.0",
            "id": 5,
            "method": "textDocument/definition",
            "params": {
                "textDocument": {
                    "uri": "file:///workspace-a/src/App.tsx",
                },
                "position": object_key_position,
            },
        }),
    );
    assert_eq!(
        object_key_definition
            .as_ref()
            .and_then(|value| value.pointer("/result/0/range/start/line")),
        Some(&json!(4)),
    );
    Ok(())
}