use crate::syntax_tokens::semantic_tokens; use crate::utils::UriUtils; use crate::workspace::{Workspace, start_workspace_thread}; use std::sync::Arc; use tokio::sync::RwLock; use tower_lsp_server::jsonrpc::Result; use tower_lsp_server::ls_types::request::{GotoDeclarationParams, GotoDeclarationResponse}; use tower_lsp_server::{Client, LspService, Server}; use tower_lsp_server::{LanguageServer, ls_types::*}; mod syntax_tokens; mod utils; mod workspace; struct Backend { client: Client, workspaces: RwLock>>, } impl LanguageServer for Backend { async fn initialize(&self, params: InitializeParams) -> Result { let mut workspaces = self.workspaces.write().await; for workspace in workspaces.drain(..) { workspace.close().await; } for folder in params.workspace_folders.iter().flatten() { workspaces .push(start_workspace_thread(folder.uri.clone(), self.client.clone()).unwrap()); } Ok(InitializeResult { capabilities: ServerCapabilities { hover_provider: Some(HoverProviderCapability::Simple(true)), definition_provider: Some(OneOf::Left(true)), declaration_provider: Some(DeclarationCapability::Simple(true)), text_document_sync: Some(TextDocumentSyncCapability::Options( TextDocumentSyncOptions { save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions { include_text: Some(false), })), change: Some(TextDocumentSyncKind::INCREMENTAL), ..Default::default() }, )), semantic_tokens_provider: Some( SemanticTokensServerCapabilities::SemanticTokensOptions( SemanticTokensOptions { legend: SemanticTokensLegend { token_types: semantic_tokens::TOKENS.to_vec(), token_modifiers: vec![], }, full: Some(SemanticTokensFullOptions::Bool(true)), range: Some(false), ..Default::default() }, ), ), ..Default::default() }, ..Default::default() }) } async fn initialized(&self, _: InitializedParams) { self.client .log_message(MessageType::INFO, "server initialized!") .await; } async fn hover(&self, params: HoverParams) -> Result> { if let Some(w) = self .find_workspace(¶ms.text_document_position_params.text_document.uri) .await { return Ok(w.hover(params).await); } Ok(None) } async fn goto_declaration( &self, params: GotoDeclarationParams, ) -> Result> { if let Some(w) = self .find_workspace(¶ms.text_document_position_params.text_document.uri) .await { return Ok(w.go_to_declaration(params).await); } Ok(None) } async fn goto_definition( &self, params: GotoDeclarationParams, ) -> Result> { if let Some(w) = self .find_workspace(¶ms.text_document_position_params.text_document.uri) .await { return Ok(w.go_to_declaration(params).await); } Ok(None) } async fn did_change(&self, params: DidChangeTextDocumentParams) { if let Some(w) = self.find_workspace(¶ms.text_document.uri).await { w.file_changed(params).await; } } async fn did_save(&self, params: DidSaveTextDocumentParams) { if let Some(w) = self.find_workspace(¶ms.text_document.uri).await { w.reload().await; } } async fn semantic_tokens_full( &self, params: SemanticTokensParams, ) -> Result> { if let Some(w) = self.find_workspace(¶ms.text_document.uri).await { return Ok(w.semantic_tokens(params).await); } Ok(None) } async fn shutdown(&self) -> Result<()> { let mut workspaces = self.workspaces.write().await; for workspace in workspaces.drain(..) { workspace.close().await; } Ok(()) } } impl Backend { pub async fn find_workspace(&self, file: &Uri) -> Option> { let workspaces = self.workspaces.read().await; let path = file.strip_header(); workspaces .iter() .find(|w| path.starts_with(&w.base)) .cloned() } } #[tokio::main] async fn main() { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); let (service, socket) = LspService::new(|client| Backend { client, workspaces: RwLock::default(), }); Server::new(stdin, stdout, socket).serve(service).await; }