Show status of LSP actions (#9818)
Fixes #4380 Parts im still unsure about: - [x] where exactly I should call `on_lsp_start`/`on_lsp_end` - [x] how to handle things better than `let is_references = TypeId::of::<R>() == TypeId::of::<GetReferences>();`, which feels very janky - [x] I want to have the message be something like `"Finding references to [...]"` instead of just `textDocument/references`, but I'm not sure how to retrieve the name of the symbol that's being queried - [ ] I think the bulk of the runtime is occupied by `let result = language_server.request::<R::LspRequest>(lsp_params).await;`, but since `ModelContext` isn't passed into it, I'm not sure how to update progress from within that function - [x] A good way to disambiguate between multiple calls to the same lsp function; im currently using the function name itself as the unique identifier for that request, which could create issues if multiple `textDocument/references` requests are sent in parallel Any help with these would be deeply appreciated! Release Notes: - Adds a status indicator for LSP actions --------- Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
c7961b9054
commit
4944dc9d78
3 changed files with 99 additions and 22 deletions
|
@ -4,7 +4,7 @@ pub use lsp_types::*;
|
|||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use collections::HashMap;
|
||||
use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, FutureExt};
|
||||
use futures::{channel::oneshot, io::BufWriter, select, AsyncRead, AsyncWrite, Future, FutureExt};
|
||||
use gpui::{AppContext, AsyncAppContext, BackgroundExecutor, Task};
|
||||
use parking_lot::Mutex;
|
||||
use postage::{barrier, prelude::Stream};
|
||||
|
@ -22,14 +22,15 @@ use smol::process::windows::CommandExt;
|
|||
use std::{
|
||||
ffi::OsString,
|
||||
fmt,
|
||||
future::Future,
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
pin::Pin,
|
||||
str::{self, FromStr as _},
|
||||
sync::{
|
||||
atomic::{AtomicI32, Ordering::SeqCst},
|
||||
Arc, Weak,
|
||||
},
|
||||
task::Poll,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use std::{path::Path, process::Stdio};
|
||||
|
@ -168,6 +169,37 @@ struct Error {
|
|||
message: String,
|
||||
}
|
||||
|
||||
pub trait LspRequestFuture<O>: Future<Output = O> {
|
||||
fn id(&self) -> i32;
|
||||
}
|
||||
|
||||
struct LspRequest<F> {
|
||||
id: i32,
|
||||
request: F,
|
||||
}
|
||||
|
||||
impl<F> LspRequest<F> {
|
||||
pub fn new(id: i32, request: F) -> Self {
|
||||
Self { id, request }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Future> Future for LspRequest<F> {
|
||||
type Output = F::Output;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
||||
// SAFETY: This is standard pin projection, we're pinned so our fields must be pinned.
|
||||
let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().request) };
|
||||
inner.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Future> LspRequestFuture<F::Output> for LspRequest<F> {
|
||||
fn id(&self) -> i32 {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
/// Experimental: Informs the end user about the state of the server
|
||||
///
|
||||
/// [Rust Analyzer Specification](https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/lsp-extensions.md#server-status)
|
||||
|
@ -916,7 +948,7 @@ impl LanguageServer {
|
|||
pub fn request<T: request::Request>(
|
||||
&self,
|
||||
params: T::Params,
|
||||
) -> impl Future<Output = Result<T::Result>>
|
||||
) -> impl LspRequestFuture<Result<T::Result>>
|
||||
where
|
||||
T::Result: 'static + Send,
|
||||
{
|
||||
|
@ -935,7 +967,7 @@ impl LanguageServer {
|
|||
outbound_tx: &channel::Sender<String>,
|
||||
executor: &BackgroundExecutor,
|
||||
params: T::Params,
|
||||
) -> impl 'static + Future<Output = anyhow::Result<T::Result>>
|
||||
) -> impl LspRequestFuture<Result<T::Result>>
|
||||
where
|
||||
T::Result: 'static + Send,
|
||||
{
|
||||
|
@ -984,7 +1016,7 @@ impl LanguageServer {
|
|||
let outbound_tx = outbound_tx.downgrade();
|
||||
let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse();
|
||||
let started = Instant::now();
|
||||
async move {
|
||||
LspRequest::new(id, async move {
|
||||
handle_response?;
|
||||
send?;
|
||||
|
||||
|
@ -1014,7 +1046,7 @@ impl LanguageServer {
|
|||
anyhow::bail!("LSP request timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Sends a RPC notification to the language server.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue