use async_tungstenite::tokio::accept_async; use serde_json::Value; use tokio::net::TcpListener; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer, LspService, Server}; use tracing::info; use ws_stream_tungstenite::*; #[derive(Debug)] struct Backend { client: Client, } #[tower_lsp::async_trait] impl LanguageServer for Backend { async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult { server_info: None, capabilities: ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Kind( TextDocumentSyncKind::INCREMENTAL, )), completion_provider: Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec![".".to_string()]), ..Default::default() }), execute_command_provider: Some(ExecuteCommandOptions { commands: vec!["dummy.do_something".to_string()], ..Default::default() }), workspace: Some(WorkspaceServerCapabilities { workspace_folders: Some(WorkspaceFoldersServerCapabilities { supported: Some(true), change_notifications: Some(OneOf::Left(true)), }), ..Default::default() }), ..ServerCapabilities::default() }, ..Default::default() }) } async fn initialized(&self, _: InitializedParams) { self.client .log_message(MessageType::INFO, "initialized!") .await; } async fn shutdown(&self) -> Result<()> { Ok(()) } async fn did_change_workspace_folders(&self, _: DidChangeWorkspaceFoldersParams) { self.client .log_message(MessageType::INFO, "workspace folders changed!") .await; } async fn did_change_configuration(&self, _: DidChangeConfigurationParams) { self.client .log_message(MessageType::INFO, "configuration changed!") .await; } async fn did_change_watched_files(&self, _: DidChangeWatchedFilesParams) { self.client .log_message(MessageType::INFO, "watched files have changed!") .await; } async fn execute_command(&self, _: ExecuteCommandParams) -> Result> { self.client .log_message(MessageType::INFO, "command executed!") .await; match self.client.apply_edit(WorkspaceEdit::default()).await { Ok(res) if res.applied => self.client.log_message(MessageType::INFO, "applied").await, Ok(_) => self.client.log_message(MessageType::INFO, "rejected").await, Err(err) => self.client.log_message(MessageType::ERROR, err).await, } Ok(None) } async fn did_open(&self, _: DidOpenTextDocumentParams) { self.client .log_message(MessageType::INFO, "file opened!") .await; } async fn did_change(&self, _: DidChangeTextDocumentParams) { self.client .log_message(MessageType::INFO, "file changed!") .await; } async fn did_save(&self, _: DidSaveTextDocumentParams) { self.client .log_message(MessageType::INFO, "file saved!") .await; } async fn did_close(&self, _: DidCloseTextDocumentParams) { self.client .log_message(MessageType::INFO, "file closed!") .await; } async fn completion(&self, _: CompletionParams) -> Result> { Ok(Some(CompletionResponse::Array(vec![ CompletionItem::new_simple("Hello".to_string(), "Some detail".to_string()), CompletionItem::new_simple("Bye".to_string(), "More detail".to_string()), ]))) } } #[tokio::main] async fn main() { #[cfg(feature = "runtime-agnostic")] use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; tracing_subscriber::fmt().init(); let listener = TcpListener::bind("127.0.0.1:9257").await.unwrap(); info!("Listening on {}", listener.local_addr().unwrap()); let (stream, _) = listener.accept().await.unwrap(); let (read, write) = tokio::io::split(WsStream::new(accept_async(stream).await.unwrap())); #[cfg(feature = "runtime-agnostic")] let (read, write) = (read.compat(), write.compat_write()); let (service, socket) = LspService::new(|client| Backend { client }); Server::new(read, write, socket).serve(service).await; }