summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlaž Hrastnik2021-08-31 07:03:06 +0000
committerBlaž Hrastnik2021-09-06 06:25:46 +0000
commitc3a58cdadd8be85b79d773122e807862a3da3a2f (patch)
treeb273ef61b25de933b391f08b5ec7cb660daca792
parent41f1e8e4fb4b3b387f34ef5d2e913e7ebc7fd888 (diff)
lsp: Refactor capabilities as an async OnceCell
First step in making LSP init asynchronous
-rw-r--r--helix-lsp/src/client.rs35
-rw-r--r--helix-lsp/src/lib.rs29
2 files changed, 40 insertions, 24 deletions
diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs
index d0a8183f..87078c69 100644
--- a/helix-lsp/src/client.rs
+++ b/helix-lsp/src/client.rs
@@ -13,7 +13,10 @@ use std::sync::atomic::{AtomicU64, Ordering};
use tokio::{
io::{BufReader, BufWriter},
process::{Child, Command},
- sync::mpsc::{channel, UnboundedReceiver, UnboundedSender},
+ sync::{
+ mpsc::{channel, UnboundedReceiver, UnboundedSender},
+ OnceCell,
+ },
};
#[derive(Debug)]
@@ -22,7 +25,7 @@ pub struct Client {
_process: Child,
server_tx: UnboundedSender<Payload>,
request_counter: AtomicU64,
- capabilities: Option<lsp::ServerCapabilities>,
+ pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>,
offset_encoding: OffsetEncoding,
config: Option<Value>,
}
@@ -57,14 +60,11 @@ impl Client {
_process: process,
server_tx,
request_counter: AtomicU64::new(0),
- capabilities: None,
+ capabilities: OnceCell::new(),
offset_encoding: OffsetEncoding::Utf8,
config,
};
- // TODO: async client.initialize()
- // maybe use an arc<atomic> flag
-
Ok((client, server_rx))
}
@@ -90,7 +90,7 @@ impl Client {
pub fn capabilities(&self) -> &lsp::ServerCapabilities {
self.capabilities
- .as_ref()
+ .get()
.expect("language server not yet initialized!")
}
@@ -151,7 +151,7 @@ impl Client {
}
/// Send a RPC notification to the language server.
- fn notify<R: lsp::notification::Notification>(
+ pub fn notify<R: lsp::notification::Notification>(
&self,
params: R::Params,
) -> impl Future<Output = Result<()>>
@@ -213,7 +213,7 @@ impl Client {
// General messages
// -------------------------------------------------------------------------------------------
- pub(crate) async fn initialize(&mut self) -> Result<()> {
+ pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
// TODO: delay any requests that are triggered prior to initialize
let root = find_root(None).and_then(|root| lsp::Url::from_file_path(root).ok());
@@ -281,14 +281,7 @@ impl Client {
locale: None, // TODO
};
- let response = self.request::<lsp::request::Initialize>(params).await?;
- self.capabilities = Some(response.capabilities);
-
- // next up, notify<initialized>
- self.notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
- .await?;
-
- Ok(())
+ self.request::<lsp::request::Initialize>(params).await
}
pub async fn shutdown(&self) -> Result<()> {
@@ -445,7 +438,7 @@ impl Client {
) -> Option<impl Future<Output = Result<()>>> {
// figure out what kind of sync the server supports
- let capabilities = self.capabilities.as_ref().unwrap();
+ let capabilities = self.capabilities.get().unwrap();
let sync_capabilities = match capabilities.text_document_sync {
Some(lsp::TextDocumentSyncCapability::Kind(kind))
@@ -496,7 +489,7 @@ impl Client {
text_document: lsp::TextDocumentIdentifier,
text: &Rope,
) -> Result<()> {
- let capabilities = self.capabilities.as_ref().unwrap();
+ let capabilities = self.capabilities.get().unwrap();
let include_text = match &capabilities.text_document_sync {
Some(lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions {
@@ -590,7 +583,7 @@ impl Client {
options: lsp::FormattingOptions,
work_done_token: Option<lsp::ProgressToken>,
) -> anyhow::Result<Vec<lsp::TextEdit>> {
- let capabilities = self.capabilities.as_ref().unwrap();
+ let capabilities = self.capabilities.get().unwrap();
// check if we're able to format
match capabilities.document_formatting_provider {
@@ -618,7 +611,7 @@ impl Client {
options: lsp::FormattingOptions,
work_done_token: Option<lsp::ProgressToken>,
) -> anyhow::Result<Vec<lsp::TextEdit>> {
- let capabilities = self.capabilities.as_ref().unwrap();
+ let capabilities = self.capabilities.get().unwrap();
// check if we're able to format
match capabilities.document_range_formatting_provider {
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs
index 72606b70..a118239f 100644
--- a/helix-lsp/src/lib.rs
+++ b/helix-lsp/src/lib.rs
@@ -312,17 +312,40 @@ impl Registry {
Entry::Vacant(entry) => {
// initialize a new client
let id = self.counter.fetch_add(1, Ordering::Relaxed);
- let (mut client, incoming) = Client::start(
+ let (client, incoming) = Client::start(
&config.command,
&config.args,
serde_json::from_str(language_config.config.as_deref().unwrap_or("")).ok(),
id,
)?;
- // TODO: run this async without blocking
- futures_executor::block_on(client.initialize())?;
s_incoming.push(UnboundedReceiverStream::new(incoming));
let client = Arc::new(client);
+ let _client = client.clone();
+ let initialize = tokio::spawn(async move {
+ use futures_util::TryFutureExt;
+
+ let value = _client
+ .capabilities
+ .get_or_try_init(|| {
+ _client
+ .initialize()
+ .map_ok(|response| response.capabilities)
+ })
+ .await;
+
+ value.expect("failed to initialize capabilities");
+
+ // next up, notify<initialized>
+ _client
+ .notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
+ .await
+ .unwrap();
+ });
+
+ // TODO: remove this block
+ futures_executor::block_on(initialize).map_err(|_| anyhow::anyhow!("bail"))?;
+
entry.insert((id, client.clone()));
Ok(client)
}