Skip to main content

nifty_lsp/
lib.rs

1//! A language server for Nifty: diagnostics, completion, signature help, semantic
2//! highlighting, the document outline, go to definition, find references and highlighting
3//! the name under the cursor. Every file of a project folder is analysed together, so names
4//! resolve across files.
5
6mod context;
7mod document;
8mod features;
9mod index;
10mod project;
11mod server;
12mod signature;
13mod workspace;
14
15use lsp_server::Connection;
16use lsp_types::{
17    CompletionOptions, HoverProviderCapability, InitializeParams, InitializeResult, OneOf,
18    SemanticTokensFullOptions, SemanticTokensOptions, SemanticTokensServerCapabilities,
19    ServerCapabilities, ServerInfo, SignatureHelpOptions, TextDocumentSyncCapability,
20    TextDocumentSyncKind,
21};
22
23use crate::document::Encoding;
24use crate::features::semantic_tokens;
25use crate::server::Server;
26
27pub type Error = Box<dyn std::error::Error + Send + Sync>;
28pub type Result<T> = std::result::Result<T, Error>;
29
30const SERVER_NAME: &str = "nifty-lsp";
31
32/// Serves the language server protocol over stdin and stdout until the client exits.
33///
34/// # Errors
35///
36/// Fails when the connection breaks or the client breaks the protocol.
37pub fn run() -> Result<()> {
38    let (connection, io_threads) = Connection::stdio();
39    serve(&connection)?;
40    drop(connection);
41    io_threads.join()?;
42    Ok(())
43}
44
45fn serve(connection: &Connection) -> Result<()> {
46    let (id, params) = connection.initialize_start()?;
47    let params: InitializeParams = serde_json::from_value(params)?;
48    let encoding = Encoding::negotiate(&params.capabilities);
49    let result = InitializeResult {
50        capabilities: capabilities(encoding),
51        server_info: Some(ServerInfo {
52            name: SERVER_NAME.to_owned(),
53            version: Some(env!("CARGO_PKG_VERSION").to_owned()),
54        }),
55    };
56    connection.initialize_finish(id, serde_json::to_value(result)?)?;
57    Server::new(connection, encoding).run()
58}
59
60fn capabilities(encoding: Encoding) -> ServerCapabilities {
61    ServerCapabilities {
62        position_encoding: Some(encoding.kind()),
63        text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL)),
64        semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensOptions(
65            SemanticTokensOptions {
66                legend: semantic_tokens::legend(),
67                full: Some(SemanticTokensFullOptions::Bool(true)),
68                ..SemanticTokensOptions::default()
69            },
70        )),
71        completion_provider: Some(CompletionOptions {
72            trigger_characters: Some(vec![".".to_owned()]),
73            ..CompletionOptions::default()
74        }),
75        signature_help_provider: Some(SignatureHelpOptions {
76            trigger_characters: Some(vec!["(".to_owned(), ",".to_owned()]),
77            retrigger_characters: Some(vec![":".to_owned()]),
78            ..SignatureHelpOptions::default()
79        }),
80        hover_provider: Some(HoverProviderCapability::Simple(true)),
81        document_symbol_provider: Some(OneOf::Left(true)),
82        definition_provider: Some(OneOf::Left(true)),
83        references_provider: Some(OneOf::Left(true)),
84        document_highlight_provider: Some(OneOf::Left(true)),
85        ..ServerCapabilities::default()
86    }
87}
88
89#[cfg(test)]
90mod tests;