Skip to content

Commit 9b3d4be

Browse files
bors[bot]matklad
andauthored
Merge #4726
4726: Groundwork for specifying the set of projects via config r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
2 parents 992e125 + 8baa4c5 commit 9b3d4be

File tree

6 files changed

+110
-65
lines changed

6 files changed

+110
-65
lines changed

crates/ra_project_model/src/lib.rs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::{
1414
use anyhow::{bail, Context, Result};
1515
use ra_cfg::CfgOptions;
1616
use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId};
17-
use rustc_hash::FxHashMap;
17+
use rustc_hash::{FxHashMap, FxHashSet};
1818
use serde_json::from_reader;
1919

2020
pub use crate::{
@@ -57,25 +57,25 @@ impl PackageRoot {
5757
}
5858
}
5959

60-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
61-
pub enum ProjectRoot {
60+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
61+
pub enum ProjectManifest {
6262
ProjectJson(PathBuf),
6363
CargoToml(PathBuf),
6464
}
6565

66-
impl ProjectRoot {
67-
pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> {
66+
impl ProjectManifest {
67+
pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> {
6868
if path.ends_with("rust-project.json") {
69-
return Ok(ProjectRoot::ProjectJson(path));
69+
return Ok(ProjectManifest::ProjectJson(path));
7070
}
7171
if path.ends_with("Cargo.toml") {
72-
return Ok(ProjectRoot::CargoToml(path));
72+
return Ok(ProjectManifest::CargoToml(path));
7373
}
7474
bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
7575
}
7676

77-
pub fn discover_single(path: &Path) -> Result<ProjectRoot> {
78-
let mut candidates = ProjectRoot::discover(path)?;
77+
pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
78+
let mut candidates = ProjectManifest::discover(path)?;
7979
let res = match candidates.pop() {
8080
None => bail!("no projects"),
8181
Some(it) => it,
@@ -87,12 +87,12 @@ impl ProjectRoot {
8787
Ok(res)
8888
}
8989

90-
pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> {
90+
pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> {
9191
if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
92-
return Ok(vec![ProjectRoot::ProjectJson(project_json)]);
92+
return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
9393
}
9494
return find_cargo_toml(path)
95-
.map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect());
95+
.map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
9696

9797
fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
9898
match find_in_parent_dirs(path, "Cargo.toml") {
@@ -128,16 +128,28 @@ impl ProjectRoot {
128128
.collect()
129129
}
130130
}
131+
132+
pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> {
133+
let mut res = paths
134+
.iter()
135+
.filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
136+
.flatten()
137+
.collect::<FxHashSet<_>>()
138+
.into_iter()
139+
.collect::<Vec<_>>();
140+
res.sort();
141+
res
142+
}
131143
}
132144

133145
impl ProjectWorkspace {
134146
pub fn load(
135-
root: ProjectRoot,
147+
root: ProjectManifest,
136148
cargo_features: &CargoConfig,
137149
with_sysroot: bool,
138150
) -> Result<ProjectWorkspace> {
139151
let res = match root {
140-
ProjectRoot::ProjectJson(project_json) => {
152+
ProjectManifest::ProjectJson(project_json) => {
141153
let file = File::open(&project_json).with_context(|| {
142154
format!("Failed to open json file {}", project_json.display())
143155
})?;
@@ -148,7 +160,7 @@ impl ProjectWorkspace {
148160
})?,
149161
}
150162
}
151-
ProjectRoot::CargoToml(cargo_toml) => {
163+
ProjectManifest::CargoToml(cargo_toml) => {
152164
let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
153165
.with_context(|| {
154166
format!(

crates/rust-analyzer/src/bin/main.rs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@
44
mod args;
55

66
use lsp_server::Connection;
7-
use rust_analyzer::{cli, config::Config, from_json, Result};
7+
use rust_analyzer::{
8+
cli,
9+
config::{Config, LinkedProject},
10+
from_json, Result,
11+
};
812

913
use crate::args::HelpPrinted;
14+
use ra_project_model::ProjectManifest;
1015

1116
fn main() -> Result<()> {
1217
setup_logging()?;
@@ -97,28 +102,38 @@ fn run_server() -> Result<()> {
97102
log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
98103
}
99104

100-
let cwd = std::env::current_dir()?;
101-
let root = initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
102-
103-
let workspace_roots = initialize_params
104-
.workspace_folders
105-
.map(|workspaces| {
106-
workspaces.into_iter().filter_map(|it| it.uri.to_file_path().ok()).collect::<Vec<_>>()
107-
})
108-
.filter(|workspaces| !workspaces.is_empty())
109-
.unwrap_or_else(|| vec![root]);
110-
111105
let config = {
112106
let mut config = Config::default();
113107
if let Some(value) = &initialize_params.initialization_options {
114108
config.update(value);
115109
}
116110
config.update_caps(&initialize_params.capabilities);
117111

112+
if config.linked_projects.is_empty() {
113+
let cwd = std::env::current_dir()?;
114+
let root =
115+
initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
116+
let workspace_roots = initialize_params
117+
.workspace_folders
118+
.map(|workspaces| {
119+
workspaces
120+
.into_iter()
121+
.filter_map(|it| it.uri.to_file_path().ok())
122+
.collect::<Vec<_>>()
123+
})
124+
.filter(|workspaces| !workspaces.is_empty())
125+
.unwrap_or_else(|| vec![root]);
126+
127+
config.linked_projects = ProjectManifest::discover_all(&workspace_roots)
128+
.into_iter()
129+
.map(LinkedProject::from)
130+
.collect();
131+
}
132+
118133
config
119134
};
120135

121-
rust_analyzer::main_loop(workspace_roots, config, connection)?;
136+
rust_analyzer::main_loop(config, connection)?;
122137

123138
log::info!("shutting down IO...");
124139
io_threads.join()?;

crates/rust-analyzer/src/cli/load_cargo.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use crossbeam_channel::{unbounded, Receiver};
88
use ra_db::{ExternSourceId, FileId, SourceRootId};
99
use ra_ide::{AnalysisChange, AnalysisHost};
1010
use ra_project_model::{
11-
get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace,
11+
get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest,
12+
ProjectWorkspace,
1213
};
1314
use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
1415
use rustc_hash::{FxHashMap, FxHashSet};
@@ -28,7 +29,7 @@ pub fn load_cargo(
2829
with_proc_macro: bool,
2930
) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
3031
let root = std::env::current_dir()?.join(root);
31-
let root = ProjectRoot::discover_single(&root)?;
32+
let root = ProjectManifest::discover_single(&root)?;
3233
let ws = ProjectWorkspace::load(
3334
root,
3435
&CargoConfig { load_out_dirs_from_check, ..Default::default() },

crates/rust-analyzer/src/config.rs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ use std::{ffi::OsString, path::PathBuf};
1212
use lsp_types::ClientCapabilities;
1313
use ra_flycheck::FlycheckConfig;
1414
use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig};
15-
use ra_project_model::CargoConfig;
15+
use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
1616
use serde::Deserialize;
1717

1818
#[derive(Debug, Clone)]
1919
pub struct Config {
2020
pub client_caps: ClientCapsConfig,
2121

22-
pub with_sysroot: bool,
2322
pub publish_diagnostics: bool,
2423
pub lru_capacity: Option<usize>,
2524
pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
@@ -35,6 +34,27 @@ pub struct Config {
3534
pub assist: AssistConfig,
3635
pub call_info_full: bool,
3736
pub lens: LensConfig,
37+
38+
pub with_sysroot: bool,
39+
pub linked_projects: Vec<LinkedProject>,
40+
}
41+
42+
#[derive(Debug, Clone)]
43+
pub enum LinkedProject {
44+
ProjectManifest(ProjectManifest),
45+
JsonProject(JsonProject),
46+
}
47+
48+
impl From<ProjectManifest> for LinkedProject {
49+
fn from(v: ProjectManifest) -> Self {
50+
LinkedProject::ProjectManifest(v)
51+
}
52+
}
53+
54+
impl From<JsonProject> for LinkedProject {
55+
fn from(v: JsonProject) -> Self {
56+
LinkedProject::JsonProject(v)
57+
}
3858
}
3959

4060
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -141,6 +161,7 @@ impl Default for Config {
141161
assist: AssistConfig::default(),
142162
call_info_full: true,
143163
lens: LensConfig::default(),
164+
linked_projects: Vec::new(),
144165
}
145166
}
146167
}

crates/rust-analyzer/src/main_loop.rs

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,11 @@ use std::{
1212
fmt,
1313
ops::Range,
1414
panic,
15-
path::PathBuf,
1615
sync::Arc,
1716
time::{Duration, Instant},
1817
};
1918

2019
use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
21-
use itertools::Itertools;
2220
use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
2321
use lsp_types::{
2422
DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress,
@@ -36,7 +34,7 @@ use serde::{de::DeserializeOwned, Serialize};
3634
use threadpool::ThreadPool;
3735

3836
use crate::{
39-
config::{Config, FilesWatcher},
37+
config::{Config, FilesWatcher, LinkedProject},
4038
diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask},
4139
from_proto,
4240
global_state::{GlobalState, GlobalStateSnapshot},
@@ -70,7 +68,7 @@ impl fmt::Display for LspError {
7068

7169
impl Error for LspError {}
7270

73-
pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> {
71+
pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
7472
log::info!("initial config: {:#?}", config);
7573

7674
// Windows scheduler implements priority boosts: if thread waits for an
@@ -95,29 +93,24 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
9593
let mut loop_state = LoopState::default();
9694
let mut global_state = {
9795
let workspaces = {
98-
// FIXME: support dynamic workspace loading.
99-
let project_roots: FxHashSet<_> = ws_roots
100-
.iter()
101-
.filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
102-
.flatten()
103-
.collect();
104-
105-
if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
96+
if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found {
10697
show_message(
10798
lsp_types::MessageType::Error,
108-
format!(
109-
"rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
110-
ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
111-
),
99+
"rust-analyzer failed to discover workspace".to_string(),
112100
&connection.sender,
113101
);
114102
};
115103

116-
project_roots
117-
.into_iter()
104+
config
105+
.linked_projects
106+
.iter()
107+
.filter_map(|project| match project {
108+
LinkedProject::ProjectManifest(it) => Some(it),
109+
LinkedProject::JsonProject(_) => None,
110+
})
118111
.filter_map(|root| {
119112
ra_project_model::ProjectWorkspace::load(
120-
root,
113+
root.clone(),
121114
&config.cargo,
122115
config.with_sysroot,
123116
)

crates/rust-analyzer/tests/heavy_tests/support.rs

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ use serde_json::{to_string_pretty, Value};
1919
use tempfile::TempDir;
2020
use test_utils::{find_mismatch, parse_fixture};
2121

22+
use ra_project_model::ProjectManifest;
2223
use rust_analyzer::{
23-
config::{ClientCapsConfig, Config},
24+
config::{ClientCapsConfig, Config, LinkedProject},
2425
main_loop,
2526
};
2627

@@ -42,7 +43,7 @@ impl<'a> Project<'a> {
4243
self
4344
}
4445

45-
pub fn root(mut self, path: &str) -> Project<'a> {
46+
pub(crate) fn root(mut self, path: &str) -> Project<'a> {
4647
self.roots.push(path.into());
4748
self
4849
}
@@ -74,7 +75,16 @@ impl<'a> Project<'a> {
7475
paths.push((path, entry.text));
7576
}
7677

77-
let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect();
78+
let mut roots =
79+
self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect::<Vec<_>>();
80+
if roots.is_empty() {
81+
roots.push(tmp_dir.path().to_path_buf());
82+
}
83+
let linked_projects = roots
84+
.into_iter()
85+
.map(|it| ProjectManifest::discover_single(&it).unwrap())
86+
.map(LinkedProject::from)
87+
.collect::<Vec<_>>();
7888

7989
let mut config = Config {
8090
client_caps: ClientCapsConfig {
@@ -84,14 +94,15 @@ impl<'a> Project<'a> {
8494
..Default::default()
8595
},
8696
with_sysroot: self.with_sysroot,
97+
linked_projects,
8798
..Config::default()
8899
};
89100

90101
if let Some(f) = &self.config {
91102
f(&mut config)
92103
}
93104

94-
Server::new(tmp_dir, config, roots, paths)
105+
Server::new(tmp_dir, config, paths)
95106
}
96107
}
97108

@@ -109,20 +120,12 @@ pub struct Server {
109120
}
110121

111122
impl Server {
112-
fn new(
113-
dir: TempDir,
114-
config: Config,
115-
roots: Vec<PathBuf>,
116-
files: Vec<(PathBuf, String)>,
117-
) -> Server {
118-
let path = dir.path().to_path_buf();
119-
120-
let roots = if roots.is_empty() { vec![path] } else { roots };
123+
fn new(dir: TempDir, config: Config, files: Vec<(PathBuf, String)>) -> Server {
121124
let (connection, client) = Connection::memory();
122125

123126
let _thread = jod_thread::Builder::new()
124127
.name("test server".to_string())
125-
.spawn(move || main_loop(roots, config, connection).unwrap())
128+
.spawn(move || main_loop(config, connection).unwrap())
126129
.expect("failed to spawn a thread");
127130

128131
let res =

0 commit comments

Comments
 (0)