-
Notifications
You must be signed in to change notification settings - Fork 23
Move LSP's tokio::Runtime and Client to RMain
#193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
eb510bc
20210cc
1e92bf7
20c9529
0307a7a
f57aa38
f467711
cf346b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| // | ||
| // backend.rs | ||
| // | ||
| // Copyright (C) 2022 Posit Software, PBC. All rights reserved. | ||
| // Copyright (C) 2022-2024 Posit Software, PBC. All rights reserved. | ||
| // | ||
| // | ||
|
|
||
|
|
@@ -12,17 +12,18 @@ use std::sync::Arc; | |
|
|
||
| use crossbeam::channel::Sender; | ||
| use dashmap::DashMap; | ||
| use log::*; | ||
| use parking_lot::Mutex; | ||
| use serde_json::Value; | ||
| use stdext::result::ResultOrLog; | ||
| use stdext::*; | ||
| use tokio::net::TcpListener; | ||
| use tokio::runtime::Runtime; | ||
| use tower_lsp::jsonrpc::Result; | ||
| use tower_lsp::lsp_types::request::GotoImplementationParams; | ||
| use tower_lsp::lsp_types::request::GotoImplementationResponse; | ||
| use tower_lsp::lsp_types::*; | ||
| use tower_lsp::Client; | ||
| use tower_lsp::ClientSocket; | ||
| use tower_lsp::LanguageServer; | ||
| use tower_lsp::LspService; | ||
| use tower_lsp::Server; | ||
|
|
@@ -34,7 +35,6 @@ use crate::lsp::diagnostics; | |
| use crate::lsp::document_context::DocumentContext; | ||
| use crate::lsp::documents::Document; | ||
| use crate::lsp::documents::DOCUMENT_INDEX; | ||
| use crate::lsp::globals; | ||
| use crate::lsp::help_topic; | ||
| use crate::lsp::hover::hover; | ||
| use crate::lsp::indexer; | ||
|
|
@@ -89,12 +89,12 @@ impl Backend { | |
| // then use that; otherwise, try to read the document from the provided | ||
| // path and use that instead. | ||
| let uri = unwrap!(Url::from_file_path(path), Err(_) => { | ||
| info!("couldn't construct uri from {}; reading from disk instead", path.display()); | ||
| log::info!("couldn't construct uri from {}; reading from disk instead", path.display()); | ||
| return fallback(); | ||
| }); | ||
|
|
||
| let document = unwrap!(self.documents.get(&uri), None => { | ||
| info!("no document for uri {}; reading from disk instead", uri); | ||
| log::info!("no document for uri {}; reading from disk instead", uri); | ||
| return fallback(); | ||
| }); | ||
|
|
||
|
|
@@ -214,7 +214,7 @@ impl LanguageServer for Backend { | |
| backend_trace!(self, "symbol({:?})", params); | ||
|
|
||
| let response = unwrap!(symbols::symbols(self, ¶ms), Err(error) => { | ||
| error!("{:?}", error); | ||
| log::error!("{:?}", error); | ||
| return Ok(None); | ||
| }); | ||
|
|
||
|
|
@@ -228,7 +228,7 @@ impl LanguageServer for Backend { | |
| backend_trace!(self, "document_symbols({})", params.text_document.uri); | ||
|
|
||
| let response = unwrap!(symbols::document_symbols(self, ¶ms), Err(error) => { | ||
| error!("{:?}", error); | ||
| log::error!("{:?}", error); | ||
| return Ok(None); | ||
| }); | ||
|
|
||
|
|
@@ -280,15 +280,15 @@ impl LanguageServer for Backend { | |
| if let Ok(path) = uri.to_file_path() { | ||
| let path = Path::new(&path); | ||
| if let Err(error) = indexer::update(&doc, &path) { | ||
| error!("{:?}", error); | ||
| log::error!("{:?}", error); | ||
| } | ||
| } | ||
|
|
||
| // publish diagnostics - but only publish them if the version of | ||
| // the document now matches the version of the change after applying | ||
| // it in `on_did_change()` | ||
| if params.text_document.version == version { | ||
| diagnostics::enqueue_diagnostics(self.clone(), uri.clone(), version).await; | ||
| diagnostics::enqueue_diagnostics(self.clone(), uri.clone(), version); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -382,7 +382,7 @@ impl LanguageServer for Backend { | |
|
|
||
| // unwrap errors | ||
| let result = unwrap!(result, Err(error) => { | ||
| error!("{:?}", error); | ||
| log::error!("{:?}", error); | ||
| return Ok(None); | ||
| }); | ||
|
|
||
|
|
@@ -413,7 +413,7 @@ impl LanguageServer for Backend { | |
|
|
||
| // unwrap errors | ||
| let result = unwrap!(result, Err(error) => { | ||
| error!("{:?}", error); | ||
| log::error!("{:?}", error); | ||
| return Ok(None); | ||
| }); | ||
|
|
||
|
|
@@ -440,7 +440,7 @@ impl LanguageServer for Backend { | |
|
|
||
| // build goto definition context | ||
| let result = unwrap!(unsafe { goto_definition(&document, params) }, Err(error) => { | ||
| error!("{}", error); | ||
| log::error!("{}", error); | ||
| return Ok(None); | ||
| }); | ||
|
|
||
|
|
@@ -453,7 +453,7 @@ impl LanguageServer for Backend { | |
| ) -> Result<Option<GotoImplementationResponse>> { | ||
| backend_trace!(self, "goto_implementation({:?})", params); | ||
| let _ = params; | ||
| error!("Got a textDocument/implementation request, but it is not implemented"); | ||
| log::error!("Got a textDocument/implementation request, but it is not implemented"); | ||
| return Ok(None); | ||
| } | ||
|
|
||
|
|
@@ -492,39 +492,57 @@ impl LanguageServer for Backend { | |
| // https://github.com/Microsoft/vscode-languageserver-node/blob/18fad46b0e8085bb72e1b76f9ea23a379569231a/client/src/common/client.ts#L701-L752 | ||
| impl Backend { | ||
| async fn notification(&self, params: Option<Value>) { | ||
| info!("Received Positron notification: {:?}", params); | ||
| log::info!("Received Positron notification: {:?}", params); | ||
| } | ||
| } | ||
|
|
||
| #[tokio::main] | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No more attribute because we manage this manually now by creating the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, this attribute seems a bit too magic/mysterious when you're not familiar with tokio |
||
| pub async fn start_lsp(address: String, conn_init_tx: Sender<bool>) { | ||
| #[cfg(feature = "runtime-agnostic")] | ||
| use tokio_util::compat::TokioAsyncReadCompatExt; | ||
| #[cfg(feature = "runtime-agnostic")] | ||
| use tokio_util::compat::TokioAsyncWriteCompatExt; | ||
|
|
||
| debug!("Connecting to LSP at '{}'", &address); | ||
| let listener = TcpListener::bind(&address).await.unwrap(); | ||
|
|
||
| // Notify frontend that we are ready to accept connections | ||
| conn_init_tx | ||
| .send(true) | ||
| .or_log_warning("Couldn't send LSP server init notification"); | ||
|
|
||
| let (stream, _) = listener.accept().await.unwrap(); | ||
| debug!("Connected to LSP at '{}'", address); | ||
| let (read, write) = tokio::io::split(stream); | ||
| pub fn start_lsp( | ||
| runtime: Arc<Runtime>, | ||
| service: LspService<Backend>, | ||
| socket: ClientSocket, | ||
| address: String, | ||
| conn_init_tx: Sender<bool>, | ||
| ) { | ||
| runtime.block_on(async { | ||
| #[cfg(feature = "runtime-agnostic")] | ||
| use tokio_util::compat::TokioAsyncReadCompatExt; | ||
| #[cfg(feature = "runtime-agnostic")] | ||
| use tokio_util::compat::TokioAsyncWriteCompatExt; | ||
|
|
||
| log::trace!("Connecting to LSP at '{}'", &address); | ||
| let listener = TcpListener::bind(&address).await.unwrap(); | ||
|
|
||
| // Notify frontend that we are ready to accept connections | ||
| conn_init_tx | ||
| .send(true) | ||
| .or_log_warning("Couldn't send LSP server init notification"); | ||
|
|
||
| let (stream, _) = listener.accept().await.unwrap(); | ||
| log::trace!("Connected to LSP at '{}'", address); | ||
| let (read, write) = tokio::io::split(stream); | ||
|
|
||
| #[cfg(feature = "runtime-agnostic")] | ||
| let (read, write) = (read.compat(), write.compat_write()); | ||
|
|
||
| let server = Server::new(read, write, socket); | ||
| server.serve(service).await; | ||
|
|
||
| log::trace!( | ||
| "LSP thread exiting gracefully after connection closed ({:?}).", | ||
| address | ||
| ); | ||
| }) | ||
| } | ||
|
|
||
| #[cfg(feature = "runtime-agnostic")] | ||
| let (read, write) = (read.compat(), write.compat_write()); | ||
| pub fn build_lsp_service() -> (LspService<Backend>, ClientSocket, Client) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This encapsulates the "trick" for getting access to the |
||
| let mut client = None; | ||
|
|
||
| let init = |client: Client| { | ||
| // initialize shared globals (needed for R callbacks) | ||
| globals::initialize(client.clone()); | ||
| let init = |client_callback: Client| { | ||
| client = Some(client_callback.clone()); | ||
|
|
||
| // create backend | ||
| let backend = Backend { | ||
| client, | ||
| client: client_callback, | ||
| documents: DOCUMENT_INDEX.clone(), | ||
| workspace: Arc::new(Mutex::new(Workspace::default())), | ||
| }; | ||
|
|
@@ -541,9 +559,7 @@ pub async fn start_lsp(address: String, conn_init_tx: Sender<bool>) { | |
| .custom_method("positron/notification", Backend::notification) | ||
| .finish(); | ||
|
|
||
| Server::new(read, write, socket).serve(service).await; | ||
| debug!( | ||
| "LSP thread exiting gracefully after connection closed ({:?}).", | ||
| address | ||
| ); | ||
| let client = client.expect("`Client` should be initialized by the `build()` callback."); | ||
|
|
||
| (service, socket, client) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -85,7 +85,7 @@ impl<'a> DiagnosticContext<'a> { | |
| } | ||
| } | ||
|
|
||
| pub async fn enqueue_diagnostics(backend: Backend, uri: Url, version: i32) { | ||
| pub fn enqueue_diagnostics(backend: Backend, uri: Url, version: i32) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Happened to realize along the way that this doesn't need to be async
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was also thinking we could remove the unnecessary |
||
| // log::trace!("[diagnostics({version}, {uri})] Spawning task to enqueue diagnostics."); | ||
|
|
||
| // Spawn a task to enqueue diagnostics. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be called
tokio_runtime? I assume we'll share with entities other than the LSP if tokio becomes needed in other parts of ark?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Happy to rename this in the future, but right now I'm not 100% sure if we will use the same tokio runtime everywhere or not. For example, in
help_proxy.rswe have another separate runtime already