@@ -313,6 +313,15 @@ impl Attach {
}
}
+/// Determines what gets sent out as a workspace folders content
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum WorkspaceFoldersContent {
+ /// Send out a single entry with the root of the workspace.
+ WorktreeRoot,
+ /// Send out a list of subproject roots.
+ SubprojectRoots,
+}
+
/// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application
// e.g. to display a notification or fetch data from the web.
#[async_trait]
@@ -606,6 +615,13 @@ pub trait LspAdapter: 'static + Send + Sync {
Attach::Shared
}
+ /// Determines whether a language server supports workspace folders.
+ ///
+ /// And does not trip over itself in the process.
+ fn workspace_folders_content(&self) -> WorkspaceFoldersContent {
+ WorkspaceFoldersContent::SubprojectRoots
+ }
+
fn manifest_name(&self) -> Option<ManifestName> {
None
}
@@ -4,13 +4,13 @@ use async_trait::async_trait;
use collections::HashMap;
use gpui::{App, Task};
use gpui::{AsyncApp, SharedString};
-use language::Toolchain;
use language::ToolchainList;
use language::ToolchainLister;
use language::language_settings::language_settings;
use language::{ContextLocation, LanguageToolchainStore};
use language::{ContextProvider, LspAdapter, LspAdapterDelegate};
use language::{LanguageName, ManifestName, ManifestProvider, ManifestQuery};
+use language::{Toolchain, WorkspaceFoldersContent};
use lsp::LanguageServerBinary;
use lsp::LanguageServerName;
use node_runtime::NodeRuntime;
@@ -400,6 +400,9 @@ impl LspAdapter for PythonLspAdapter {
fn manifest_name(&self) -> Option<ManifestName> {
Some(SharedString::new_static("pyproject.toml").into())
}
+ fn workspace_folders_content(&self) -> WorkspaceFoldersContent {
+ WorkspaceFoldersContent::WorktreeRoot
+ }
}
async fn get_cached_server_binary(
@@ -1282,6 +1285,9 @@ impl LspAdapter for PyLspAdapter {
fn manifest_name(&self) -> Option<ManifestName> {
Some(SharedString::new_static("pyproject.toml").into())
}
+ fn workspace_folders_content(&self) -> WorkspaceFoldersContent {
+ WorkspaceFoldersContent::WorktreeRoot
+ }
}
#[cfg(test)]
@@ -29,7 +29,7 @@ use std::{
ffi::{OsStr, OsString},
fmt,
io::Write,
- ops::{Deref, DerefMut},
+ ops::DerefMut,
path::PathBuf,
pin::Pin,
sync::{
@@ -100,7 +100,7 @@ pub struct LanguageServer {
io_tasks: Mutex<Option<(Task<Option<()>>, Task<Option<()>>)>>,
output_done_rx: Mutex<Option<barrier::Receiver>>,
server: Arc<Mutex<Option<Child>>>,
- workspace_folders: Arc<Mutex<BTreeSet<Url>>>,
+ workspace_folders: Option<Arc<Mutex<BTreeSet<Url>>>>,
root_uri: Url,
}
@@ -307,7 +307,7 @@ impl LanguageServer {
binary: LanguageServerBinary,
root_path: &Path,
code_action_kinds: Option<Vec<CodeActionKind>>,
- workspace_folders: Arc<Mutex<BTreeSet<Url>>>,
+ workspace_folders: Option<Arc<Mutex<BTreeSet<Url>>>>,
cx: &mut AsyncApp,
) -> Result<Self> {
let working_dir = if root_path.is_dir() {
@@ -381,7 +381,7 @@ impl LanguageServer {
code_action_kinds: Option<Vec<CodeActionKind>>,
binary: LanguageServerBinary,
root_uri: Url,
- workspace_folders: Arc<Mutex<BTreeSet<Url>>>,
+ workspace_folders: Option<Arc<Mutex<BTreeSet<Url>>>>,
cx: &mut AsyncApp,
on_unhandled_notification: F,
) -> Self
@@ -595,16 +595,26 @@ impl LanguageServer {
}
pub fn default_initialize_params(&self, pull_diagnostics: bool, cx: &App) -> InitializeParams {
- let workspace_folders = self
- .workspace_folders
- .lock()
- .iter()
- .cloned()
- .map(|uri| WorkspaceFolder {
- name: Default::default(),
- uri,
- })
- .collect::<Vec<_>>();
+ let workspace_folders = self.workspace_folders.as_ref().map_or_else(
+ || {
+ vec![WorkspaceFolder {
+ name: Default::default(),
+ uri: self.root_uri.clone(),
+ }]
+ },
+ |folders| {
+ folders
+ .lock()
+ .iter()
+ .cloned()
+ .map(|uri| WorkspaceFolder {
+ name: Default::default(),
+ uri,
+ })
+ .collect()
+ },
+ );
+
#[allow(deprecated)]
InitializeParams {
process_id: None,
@@ -1315,7 +1325,10 @@ impl LanguageServer {
return;
}
- let is_new_folder = self.workspace_folders.lock().insert(uri.clone());
+ let Some(workspace_folders) = self.workspace_folders.as_ref() else {
+ return;
+ };
+ let is_new_folder = workspace_folders.lock().insert(uri.clone());
if is_new_folder {
let params = DidChangeWorkspaceFoldersParams {
event: WorkspaceFoldersChangeEvent {
@@ -1345,7 +1358,10 @@ impl LanguageServer {
{
return;
}
- let was_removed = self.workspace_folders.lock().remove(&uri);
+ let Some(workspace_folders) = self.workspace_folders.as_ref() else {
+ return;
+ };
+ let was_removed = workspace_folders.lock().remove(&uri);
if was_removed {
let params = DidChangeWorkspaceFoldersParams {
event: WorkspaceFoldersChangeEvent {
@@ -1360,7 +1376,10 @@ impl LanguageServer {
}
}
pub fn set_workspace_folders(&self, folders: BTreeSet<Url>) {
- let mut workspace_folders = self.workspace_folders.lock();
+ let Some(workspace_folders) = self.workspace_folders.as_ref() else {
+ return;
+ };
+ let mut workspace_folders = workspace_folders.lock();
let old_workspace_folders = std::mem::take(&mut *workspace_folders);
let added: Vec<_> = folders
@@ -1389,8 +1408,11 @@ impl LanguageServer {
}
}
- pub fn workspace_folders(&self) -> impl Deref<Target = BTreeSet<Url>> + '_ {
- self.workspace_folders.lock()
+ pub fn workspace_folders(&self) -> BTreeSet<Url> {
+ self.workspace_folders.as_ref().map_or_else(
+ || BTreeSet::from_iter([self.root_uri.clone()]),
+ |folders| folders.lock().clone(),
+ )
}
pub fn register_buffer(
@@ -1535,7 +1557,7 @@ impl FakeLanguageServer {
None,
binary.clone(),
root,
- workspace_folders.clone(),
+ Some(workspace_folders.clone()),
cx,
|_| {},
);
@@ -1554,7 +1576,7 @@ impl FakeLanguageServer {
None,
binary,
Self::root_path(),
- workspace_folders,
+ Some(workspace_folders),
cx,
move |msg| {
notifications_tx
@@ -46,6 +46,7 @@ use language::{
DiagnosticEntry, DiagnosticSet, DiagnosticSourceKind, Diff, File as _, Language, LanguageName,
LanguageRegistry, LanguageToolchainStore, LocalFile, LspAdapter, LspAdapterDelegate, Patch,
PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, Unclipped,
+ WorkspaceFoldersContent,
language_settings::{
FormatOnSave, Formatter, LanguageSettings, SelectedFormatter, language_settings,
},
@@ -217,6 +218,7 @@ impl LocalLspStore {
let binary = self.get_language_server_binary(adapter.clone(), delegate.clone(), true, cx);
let pending_workspace_folders: Arc<Mutex<BTreeSet<Url>>> = Default::default();
+
let pending_server = cx.spawn({
let adapter = adapter.clone();
let server_name = adapter.name.clone();
@@ -242,14 +244,18 @@ impl LocalLspStore {
return Ok(server);
}
+ let code_action_kinds = adapter.code_action_kinds();
lsp::LanguageServer::new(
stderr_capture,
server_id,
server_name,
binary,
&root_path,
- adapter.code_action_kinds(),
- pending_workspace_folders,
+ code_action_kinds,
+ Some(pending_workspace_folders).filter(|_| {
+ adapter.adapter.workspace_folders_content()
+ == WorkspaceFoldersContent::SubprojectRoots
+ }),
cx,
)
}
@@ -575,8 +581,7 @@ impl LocalLspStore {
};
let root = server.workspace_folders();
Ok(Some(
- root.iter()
- .cloned()
+ root.into_iter()
.map(|uri| WorkspaceFolder {
uri,
name: Default::default(),