0

I'm currently writing a language server in rust and have the following problem: I want to add code completion sensitive to the code already been written. I followed the the example for the crate lsp-server I'm using. How can I get the text of the file currently in edit?

Sadly there is not much documentation. I've already tried to look my way through the GitHub repository of rust-analyzer but it is to overwhelming and I can't find a solution. I know how to get the file path but when I open the file I only get the saved version not the edited one.

Currently my code does not much differ from the example, but I add it anyway:

mod completion_data;

use std::error::Error;

use lsp_server::{Connection, ExtractError, Message, RequestId, Response};
use lsp_types::notification::{Initialized, Notification};
use lsp_types::request::{
    CodeLensRequest, Completion, DocumentColor, DocumentHighlightRequest, HoverRequest, Initialize,
    Request, ShowDocument,
};
use lsp_types::{
    CompletionItem, CompletionList, CompletionOptions, TextDocumentChangeRegistrationOptions,
    TextDocumentIdentifier, TextDocumentSyncCapability, TextDocumentSyncOptions, WorkspaceEdit,
};
use lsp_types::{InitializeParams, ServerCapabilities};
use serde::de::DeserializeOwned;

use crate::completion_data::get_completion_items;

fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
    // Note that  we must have our logging only write out to stderr.
    eprintln!("starting generic LSP server");

    // Create the transport. Includes the stdio (stdin and stdout) versions but this could
    // also be implemented to use sockets or HTTP.
    let (connection, io_threads) = Connection::stdio();

    // Run the server and wait for the two threads to end (typically by trigger LSP Exit event).
    let server_capabilities = serde_json::to_value(&ServerCapabilities {
        completion_provider: Some(CompletionOptions {
            trigger_characters: Some(vec!["&".to_string()]),
            ..Default::default()
        }),
        ..Default::default()
    })
    .unwrap();
    let initialization_params = connection.initialize(server_capabilities)?;
    main_loop(connection, initialization_params)?;
    io_threads.join()?;

    // Shut down gracefully.
    eprintln!("shutting down server");
    Ok(())
}

fn main_loop(
    connection: Connection,
    params: serde_json::Value,
) -> Result<(), Box<dyn Error + Sync + Send>> {
    let _params: InitializeParams = serde_json::from_value(params).unwrap();
    for msg in &connection.receiver {
        match msg {
            Message::Request(request) => {
                if connection.handle_shutdown(&request)? {
                    return Ok(());
                }

                match request.method.as_str() {
                    Completion::METHOD => {
                        let (id, compilation) = cast::<Completion>(request)?;
                        //TODO Get the current text and based on it suggest completion
                    }
                    _ => {}
                }
            }
            Message::Response(resp) => {
                eprintln!("got response: {:?}", resp);
            }
            Message::Notification(not) => {
                eprintln!("got notification: {:?}", not);
            }
        }
    }
    Ok(())
}

fn cast<R>(
    req: lsp_server::Request,
) -> Result<(RequestId, R::Params), ExtractError<lsp_server::Request>>
where
    R: lsp_types::request::Request,
    R::Params: serde::de::DeserializeOwned,
{
    req.extract(R::METHOD)
}
  • You may want give tower-lsp a try, it seems to both be better documented as well as having a more friendly API that doesn't include using serde manually. – Ivan C Oct 25 '22 at 13:32

0 Answers0