php-lsp 0.2.0

A PHP Language Server Protocol implementation
Documentation
mod common;

use common::TestServer;

#[tokio::test]
async fn completion_after_initialize() {
    let mut server = TestServer::new().await;
    let opened = server.open_fixture("<?php\nclas$0").await;
    let c = opened.cursor();

    let resp = server.completion(&c.path, c.line, c.character).await;

    assert!(resp["error"].is_null(), "completion error: {resp:?}");
    let items = match &resp["result"] {
        v if v.is_array() => v.as_array().unwrap().clone(),
        v if v["items"].is_array() => v["items"].as_array().unwrap().clone(),
        other => panic!("completion must return a concrete list, got: {other:?}"),
    };
    assert!(
        !items.is_empty(),
        "top-level completion after `clas` must offer at least the `class` keyword"
    );
    assert!(
        items.iter().any(|i| i["label"].as_str() == Some("class")),
        "`class` keyword must be among completions: {items:?}"
    );
}

#[tokio::test]
async fn completion_resolve_returns_item() {
    let mut server = TestServer::new().await;
    let opened = server
        .open_fixture(
            r#"<?php
function resolveMe(): void {}
resolveM$0
"#,
        )
        .await;
    let c = opened.cursor();

    let comp = server.completion(&c.path, c.line, c.character).await;
    let items = match &comp["result"] {
        v if v.is_array() => v.as_array().unwrap().to_vec(),
        v if v["items"].is_array() => v["items"].as_array().unwrap().to_vec(),
        _ => vec![],
    };
    assert!(
        !items.is_empty(),
        "expected completions for 'resolveM' prefix: {:?}",
        comp["result"]
    );

    let resolve_me = items
        .iter()
        .find(|i| i["label"].as_str() == Some("resolveMe"))
        .cloned()
        .expect("resolveMe must appear in completions for its own prefix");

    let resp = server.completion_resolve(resolve_me).await;

    assert!(
        resp["error"].is_null(),
        "completionItem/resolve error: {resp:?}"
    );
    assert!(resp["result"].is_object(), "expected resolved item object");
    let detail = resp["result"]["detail"].as_str().unwrap_or("");
    assert!(
        detail.contains("resolveMe"),
        "resolved item must have detail populated with the function signature: {:?}",
        resp["result"]
    );
}