achitek-ls 0.2.0

achitekfile language server
Documentation
use crate::utils;
use achitek_ls::{
    handlers::request::handle_completion,
    server::{Document, Documents, ServerState},
};
use lsp_server::{Connection, Request, RequestId};
use lsp_types::{
    CompletionParams, CompletionResponse, Position, TextDocumentIdentifier,
    TextDocumentPositionParams, Uri,
    request::{Completion, Request as LspRequest},
};
use std::fs;

#[test]
fn completion_returns_achitekfile_prompt_attributes() -> anyhow::Result<()> {
    let (server_connection, client_connection) = Connection::memory();
    let uri: Uri = utils::TEST_URI.parse()?;
    let request_id = RequestId::from(1_i32);
    let request = Request::new(
        request_id.clone(),
        Completion::METHOD.to_owned(),
        CompletionParams {
            text_document_position: TextDocumentPositionParams {
                text_document: TextDocumentIdentifier { uri: uri.clone() },
                position: Position {
                    line: 6,
                    character: 2,
                },
            },
            work_done_progress_params: Default::default(),
            partial_result_params: Default::default(),
            context: None,
        },
    );
    let state = ServerState {
        documents: Documents::from([(
            uri.as_str().to_owned(),
            Document {
                version: 1,
                text: valid_completion_source(),
            },
        )]),
        ..Default::default()
    };
    let params = serde_json::from_value(request.params.clone())?;
    let result = handle_completion(&state, params)?;

    utils::send_response(&server_connection, &request, result)?;

    let response = utils::request_response_sink(&client_connection)?;
    assert_eq!(response.id, request_id);
    assert!(response.error.is_none());

    let result: Option<CompletionResponse> =
        serde_json::from_value(response.result.expect("response should contain a result"))?;
    let Some(CompletionResponse::Array(items)) = result else {
        panic!("expected completion item array");
    };
    assert!(items.iter().any(|item| item.label == "type"));

    Ok(())
}

#[test]
fn completion_returns_prompt_names_in_tera_expressions() -> anyhow::Result<()> {
    let temp_root = utils::temp_dir("achitek-template-completion")?;
    fs::create_dir_all(&temp_root)?;
    let achitek_path = temp_root.join("Achitekfile");
    fs::write(&achitek_path, valid_completion_source())?;
    let template_path = temp_root.join("Cargo.toml.tera");
    let template_source = "{{ pro".to_owned();
    fs::write(&template_path, &template_source)?;
    let uri = achitek_ls::server::utils::path_to_uri(&template_path)?;
    let params = CompletionParams {
        text_document_position: TextDocumentPositionParams {
            text_document: TextDocumentIdentifier { uri: uri.clone() },
            position: Position {
                line: 0,
                character: 6,
            },
        },
        work_done_progress_params: Default::default(),
        partial_result_params: Default::default(),
        context: None,
    };
    let state = ServerState {
        documents: Documents::from([(
            uri.as_str().to_owned(),
            Document {
                version: 1,
                text: template_source,
            },
        )]),
        ..Default::default()
    };

    let Some(CompletionResponse::Array(items)) = handle_completion(&state, params)? else {
        panic!("expected completion item array");
    };

    assert!(items.iter().any(|item| item.label == "project_name"));

    fs::remove_dir_all(&temp_root)?;
    Ok(())
}

fn valid_completion_source() -> String {
    indoc::indoc! {r#"
        blueprint {
          version = "1.0.0"
          name = "minimal"
        }

        prompt "project_name" {

        }
    "#}
    .to_owned()
}