Auto merge of #14603 - Veykril:workspaces, r=Veykril
fix: Deduplicate passed workspaces by top level cargo workspace they belong to Fixes https://github.com/rust-lang/rust-analyzer/issues/14571 With this we should be supporting vscode workspaces properly
This commit is contained in:
commit
e84781ad58
@ -27,7 +27,7 @@
|
|||||||
};
|
};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use serde::{de::DeserializeOwned, Deserialize};
|
use serde::{de::DeserializeOwned, Deserialize};
|
||||||
use vfs::AbsPathBuf;
|
use vfs::{AbsPath, AbsPathBuf};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
caps::completion_item_edit_resolve,
|
caps::completion_item_edit_resolve,
|
||||||
@ -535,8 +535,9 @@ fn default() -> Self {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub discovered_projects: Option<Vec<ProjectManifest>>,
|
discovered_projects: Vec<ProjectManifest>,
|
||||||
pub workspace_roots: Vec<AbsPathBuf>,
|
/// The workspace roots as registered by the LSP client
|
||||||
|
workspace_roots: Vec<AbsPathBuf>,
|
||||||
caps: lsp_types::ClientCapabilities,
|
caps: lsp_types::ClientCapabilities,
|
||||||
root_path: AbsPathBuf,
|
root_path: AbsPathBuf,
|
||||||
data: ConfigData,
|
data: ConfigData,
|
||||||
@ -742,7 +743,7 @@ pub fn new(
|
|||||||
caps,
|
caps,
|
||||||
data: ConfigData::default(),
|
data: ConfigData::default(),
|
||||||
detached_files: Vec::new(),
|
detached_files: Vec::new(),
|
||||||
discovered_projects: None,
|
discovered_projects: Vec::new(),
|
||||||
root_path,
|
root_path,
|
||||||
snippets: Default::default(),
|
snippets: Default::default(),
|
||||||
workspace_roots,
|
workspace_roots,
|
||||||
@ -755,7 +756,17 @@ pub fn rediscover_workspaces(&mut self) {
|
|||||||
if discovered.is_empty() {
|
if discovered.is_empty() {
|
||||||
tracing::error!("failed to find any projects in {:?}", &self.workspace_roots);
|
tracing::error!("failed to find any projects in {:?}", &self.workspace_roots);
|
||||||
}
|
}
|
||||||
self.discovered_projects = Some(discovered);
|
self.discovered_projects = discovered;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_workspace(&mut self, path: &AbsPath) {
|
||||||
|
if let Some(position) = self.workspace_roots.iter().position(|it| it == path) {
|
||||||
|
self.workspace_roots.remove(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_workspaces(&mut self, paths: impl Iterator<Item = AbsPathBuf>) {
|
||||||
|
self.workspace_roots.extend(paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> {
|
pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> {
|
||||||
@ -860,26 +871,20 @@ pub fn has_linked_projects(&self) -> bool {
|
|||||||
pub fn linked_projects(&self) -> Vec<LinkedProject> {
|
pub fn linked_projects(&self) -> Vec<LinkedProject> {
|
||||||
match self.data.linkedProjects.as_slice() {
|
match self.data.linkedProjects.as_slice() {
|
||||||
[] => {
|
[] => {
|
||||||
match self.discovered_projects.as_ref() {
|
let exclude_dirs: Vec<_> =
|
||||||
Some(discovered_projects) => {
|
self.data.files_excludeDirs.iter().map(|p| self.root_path.join(p)).collect();
|
||||||
let exclude_dirs: Vec<_> = self
|
self.discovered_projects
|
||||||
.data
|
|
||||||
.files_excludeDirs
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| self.root_path.join(p))
|
.filter(
|
||||||
.collect();
|
|(ProjectManifest::ProjectJson(path)
|
||||||
discovered_projects
|
| ProjectManifest::CargoToml(path))| {
|
||||||
.iter()
|
|
||||||
.filter(|(ProjectManifest::ProjectJson(path) | ProjectManifest::CargoToml(path))| {
|
|
||||||
!exclude_dirs.iter().any(|p| path.starts_with(p))
|
!exclude_dirs.iter().any(|p| path.starts_with(p))
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(LinkedProject::from)
|
.map(LinkedProject::from)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
None => Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
linked_projects => linked_projects
|
linked_projects => linked_projects
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|linked_project| match linked_project {
|
.filter_map(|linked_project| match linked_project {
|
||||||
|
@ -777,14 +777,7 @@ pub(crate) fn handle_runnables(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if !snap.config.linked_projects().is_empty()
|
if !snap.config.linked_projects().is_empty() {
|
||||||
|| !snap
|
|
||||||
.config
|
|
||||||
.discovered_projects
|
|
||||||
.as_ref()
|
|
||||||
.map(|projects| projects.is_empty())
|
|
||||||
.unwrap_or(true)
|
|
||||||
{
|
|
||||||
res.push(lsp_ext::Runnable {
|
res.push(lsp_ext::Runnable {
|
||||||
label: "cargo check --workspace".to_string(),
|
label: "cargo check --workspace".to_string(),
|
||||||
location: None,
|
location: None,
|
||||||
|
@ -908,8 +908,10 @@ fn run_flycheck(this: &mut GlobalState, vfs_path: VfsPath) -> bool {
|
|||||||
// Re-fetch workspaces if a workspace related file has changed
|
// Re-fetch workspaces if a workspace related file has changed
|
||||||
if let Some(abs_path) = vfs_path.as_path() {
|
if let Some(abs_path) = vfs_path.as_path() {
|
||||||
if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
|
if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
|
||||||
this.fetch_workspaces_queue
|
this.fetch_workspaces_queue.request_op(
|
||||||
.request_op(format!("DidSaveTextDocument {}", abs_path.display()), ());
|
format!("DidSaveTextDocument {}", abs_path.display()),
|
||||||
|
(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -972,8 +974,7 @@ fn run_flycheck(this: &mut GlobalState, vfs_path: VfsPath) -> bool {
|
|||||||
for workspace in params.event.removed {
|
for workspace in params.event.removed {
|
||||||
let Ok(path) = workspace.uri.to_file_path() else { continue };
|
let Ok(path) = workspace.uri.to_file_path() else { continue };
|
||||||
let Ok(path) = AbsPathBuf::try_from(path) else { continue };
|
let Ok(path) = AbsPathBuf::try_from(path) else { continue };
|
||||||
let Some(position) = config.workspace_roots.iter().position(|it| it == &path) else { continue };
|
config.remove_workspace(&path);
|
||||||
config.workspace_roots.remove(position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let added = params
|
let added = params
|
||||||
@ -982,10 +983,11 @@ fn run_flycheck(this: &mut GlobalState, vfs_path: VfsPath) -> bool {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|it| it.uri.to_file_path().ok())
|
.filter_map(|it| it.uri.to_file_path().ok())
|
||||||
.filter_map(|it| AbsPathBuf::try_from(it).ok());
|
.filter_map(|it| AbsPathBuf::try_from(it).ok());
|
||||||
config.workspace_roots.extend(added);
|
config.add_workspaces(added);
|
||||||
if !config.has_linked_projects() && config.detached_files().is_empty() {
|
if !config.has_linked_projects() && config.detached_files().is_empty() {
|
||||||
config.rediscover_workspaces();
|
config.rediscover_workspaces();
|
||||||
this.fetch_workspaces_queue.request_op("client workspaces changed".to_string(), ())
|
this.fetch_workspaces_queue
|
||||||
|
.request_op("client workspaces changed".to_string(), ())
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -212,6 +212,20 @@ pub(crate) fn fetch_workspaces(&mut self, cause: Cause) {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < workspaces.len() {
|
||||||
|
if let Ok(w) = &workspaces[i] {
|
||||||
|
if let Some(dupe) = workspaces[i + 1..]
|
||||||
|
.iter()
|
||||||
|
.filter_map(|it| it.as_ref().ok())
|
||||||
|
.position(|ws| ws.eq_ignore_build_data(w))
|
||||||
|
{
|
||||||
|
_ = workspaces.remove(dupe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
if !detached_files.is_empty() {
|
if !detached_files.is_empty() {
|
||||||
workspaces.push(project_model::ProjectWorkspace::load_detached_files(
|
workspaces.push(project_model::ProjectWorkspace::load_detached_files(
|
||||||
detached_files,
|
detached_files,
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
use crossbeam_channel::{after, select, Receiver};
|
use crossbeam_channel::{after, select, Receiver};
|
||||||
use lsp_server::{Connection, Message, Notification, Request};
|
use lsp_server::{Connection, Message, Notification, Request};
|
||||||
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
|
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
|
||||||
use project_model::ProjectManifest;
|
|
||||||
use rust_analyzer::{config::Config, lsp_ext, main_loop};
|
use rust_analyzer::{config::Config, lsp_ext, main_loop};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::{json, to_string_pretty, Value};
|
use serde_json::{json, to_string_pretty, Value};
|
||||||
@ -101,10 +100,6 @@ pub(crate) fn server(self) -> Server {
|
|||||||
if roots.is_empty() {
|
if roots.is_empty() {
|
||||||
roots.push(tmp_dir_path.clone());
|
roots.push(tmp_dir_path.clone());
|
||||||
}
|
}
|
||||||
let discovered_projects = roots
|
|
||||||
.into_iter()
|
|
||||||
.map(|it| ProjectManifest::discover_single(&it).unwrap())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut config = Config::new(
|
let mut config = Config::new(
|
||||||
tmp_dir_path,
|
tmp_dir_path,
|
||||||
@ -144,10 +139,10 @@ pub(crate) fn server(self) -> Server {
|
|||||||
})),
|
})),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Vec::new(),
|
roots,
|
||||||
);
|
);
|
||||||
config.discovered_projects = Some(discovered_projects);
|
|
||||||
config.update(self.config).expect("invalid config");
|
config.update(self.config).expect("invalid config");
|
||||||
|
config.rediscover_workspaces();
|
||||||
|
|
||||||
Server::new(tmp_dir, config)
|
Server::new(tmp_dir, config)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user