Add language_server_workspace_configuration to extension API (#10212)

This PR adds the ability for extensions to implement
`language_server_workspace_configuration` to provide workspace
configuration to the language server.

We've used the Dart extension as a motivating example for this, pulling
it out into an extension in the process.

Release Notes:

- Removed built-in support for Dart, in favor of making it available as
an extension. The Dart extension will be suggested for download when you
open a `.dart` file.

---------

Co-authored-by: Max <max@zed.dev>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
Marshall Bowers 2024-04-05 17:04:07 -04:00 committed by GitHub
parent 4aaf3459c4
commit c851e6edba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 586 additions and 187 deletions

View file

@ -2857,21 +2857,29 @@ impl Project {
cx.spawn(move |this, mut cx| async move {
while let Some(()) = settings_changed_rx.next().await {
let servers: Vec<_> = this.update(&mut cx, |this, _| {
this.language_servers
.values()
.filter_map(|state| match state {
LanguageServerState::Starting(_) => None,
LanguageServerState::Running {
adapter, server, ..
} => Some((adapter.clone(), server.clone())),
let servers = this.update(&mut cx, |this, cx| {
this.language_server_ids
.iter()
.filter_map(|((worktree_id, _), server_id)| {
let worktree = this.worktree_for_id(*worktree_id, cx)?;
let state = this.language_servers.get(server_id)?;
let delegate = ProjectLspAdapterDelegate::new(this, &worktree, cx);
match state {
LanguageServerState::Starting(_) => None,
LanguageServerState::Running {
adapter, server, ..
} => Some((
adapter.adapter.clone(),
server.clone(),
delegate as Arc<dyn LspAdapterDelegate>,
)),
}
})
.collect()
.collect::<Vec<_>>()
})?;
for (adapter, server) in servers {
let settings =
cx.update(|cx| adapter.workspace_configuration(server.root_path(), cx))?;
for (adapter, server, delegate) in servers {
let settings = adapter.workspace_configuration(&delegate, &mut cx).await?;
server
.notify::<lsp::notification::DidChangeConfiguration>(
@ -2985,12 +2993,13 @@ impl Project {
}
let stderr_capture = Arc::new(Mutex::new(Some(String::new())));
let lsp_adapter_delegate = ProjectLspAdapterDelegate::new(self, worktree_handle, cx);
let pending_server = match self.languages.create_pending_language_server(
stderr_capture.clone(),
language.clone(),
adapter.clone(),
Arc::clone(&worktree_path),
ProjectLspAdapterDelegate::new(self, worktree_handle, cx),
lsp_adapter_delegate.clone(),
cx,
) {
Some(pending_server) => pending_server,
@ -3018,7 +3027,7 @@ impl Project {
cx.spawn(move |this, mut cx| async move {
let result = Self::setup_and_insert_language_server(
this.clone(),
&worktree_path,
lsp_adapter_delegate,
override_options,
pending_server,
adapter.clone(),
@ -3142,7 +3151,7 @@ impl Project {
#[allow(clippy::too_many_arguments)]
async fn setup_and_insert_language_server(
this: WeakModel<Self>,
worktree_path: &Path,
delegate: Arc<dyn LspAdapterDelegate>,
override_initialization_options: Option<serde_json::Value>,
pending_server: PendingLanguageServer,
adapter: Arc<CachedLspAdapter>,
@ -3155,7 +3164,7 @@ impl Project {
this.clone(),
override_initialization_options,
pending_server,
worktree_path,
delegate,
adapter.clone(),
server_id,
cx,
@ -3185,13 +3194,16 @@ impl Project {
this: WeakModel<Self>,
override_options: Option<serde_json::Value>,
pending_server: PendingLanguageServer,
worktree_path: &Path,
delegate: Arc<dyn LspAdapterDelegate>,
adapter: Arc<CachedLspAdapter>,
server_id: LanguageServerId,
cx: &mut AsyncAppContext,
) -> Result<Arc<LanguageServer>> {
let workspace_config =
cx.update(|cx| adapter.workspace_configuration(worktree_path, cx))?;
let workspace_config = adapter
.adapter
.clone()
.workspace_configuration(&delegate, cx)
.await?;
let (language_server, mut initialization_options) = pending_server.task.await?;
let name = language_server.name();
@ -3220,14 +3232,14 @@ impl Project {
language_server
.on_request::<lsp::request::WorkspaceConfiguration, _, _>({
let adapter = adapter.clone();
let worktree_path = worktree_path.to_path_buf();
move |params, cx| {
let adapter = adapter.adapter.clone();
let delegate = delegate.clone();
move |params, mut cx| {
let adapter = adapter.clone();
let worktree_path = worktree_path.clone();
let delegate = delegate.clone();
async move {
let workspace_config =
cx.update(|cx| adapter.workspace_configuration(&worktree_path, cx))?;
adapter.workspace_configuration(&delegate, &mut cx).await?;
Ok(params
.items
.into_iter()
@ -10315,6 +10327,14 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
self.http_client.clone()
}
fn worktree_id(&self) -> u64 {
self.worktree.id().to_proto()
}
fn worktree_root_path(&self) -> &Path {
self.worktree.abs_path().as_ref()
}
async fn shell_env(&self) -> HashMap<String, String> {
self.load_shell_env().await;
self.shell_env.lock().as_ref().cloned().unwrap_or_default()

View file

@ -47,7 +47,7 @@ pub struct BinarySettings {
pub arguments: Option<Vec<String>>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct LspSettings {
pub binary: Option<BinarySettings>,