aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_project_model/src/lib.rs42
-rw-r--r--crates/rust-analyzer/src/bin/main.rs41
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs5
-rw-r--r--crates/rust-analyzer/src/config.rs25
-rw-r--r--crates/rust-analyzer/src/main_loop.rs31
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs31
6 files changed, 110 insertions, 65 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 9b30bef8d..a99612690 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -14,7 +14,7 @@ use std::{
14use anyhow::{bail, Context, Result}; 14use anyhow::{bail, Context, Result};
15use ra_cfg::CfgOptions; 15use ra_cfg::CfgOptions;
16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; 16use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId};
17use rustc_hash::FxHashMap; 17use rustc_hash::{FxHashMap, FxHashSet};
18use serde_json::from_reader; 18use serde_json::from_reader;
19 19
20pub use crate::{ 20pub use crate::{
@@ -57,25 +57,25 @@ impl PackageRoot {
57 } 57 }
58} 58}
59 59
60#[derive(Debug, Clone, PartialEq, Eq, Hash)] 60#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
61pub enum ProjectRoot { 61pub enum ProjectManifest {
62 ProjectJson(PathBuf), 62 ProjectJson(PathBuf),
63 CargoToml(PathBuf), 63 CargoToml(PathBuf),
64} 64}
65 65
66impl ProjectRoot { 66impl ProjectManifest {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { 67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> {
68 if path.ends_with("rust-project.json") { 68 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path)); 69 return Ok(ProjectManifest::ProjectJson(path));
70 } 70 }
71 if path.ends_with("Cargo.toml") { 71 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path)); 72 return Ok(ProjectManifest::CargoToml(path));
73 } 73 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) 74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
75 } 75 }
76 76
77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> { 77 pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
78 let mut candidates = ProjectRoot::discover(path)?; 78 let mut candidates = ProjectManifest::discover(path)?;
79 let res = match candidates.pop() { 79 let res = match candidates.pop() {
80 None => bail!("no projects"), 80 None => bail!("no projects"),
81 Some(it) => it, 81 Some(it) => it,
@@ -87,12 +87,12 @@ impl ProjectRoot {
87 Ok(res) 87 Ok(res)
88 } 88 }
89 89
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { 90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> {
91 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { 91 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)]);
93 } 93 }
94 return find_cargo_toml(path) 94 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());
96 96
97 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { 97 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
98 match find_in_parent_dirs(path, "Cargo.toml") { 98 match find_in_parent_dirs(path, "Cargo.toml") {
@@ -128,16 +128,28 @@ impl ProjectRoot {
128 .collect() 128 .collect()
129 } 129 }
130 } 130 }
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 }
131} 143}
132 144
133impl ProjectWorkspace { 145impl ProjectWorkspace {
134 pub fn load( 146 pub fn load(
135 root: ProjectRoot, 147 root: ProjectManifest,
136 cargo_features: &CargoConfig, 148 cargo_features: &CargoConfig,
137 with_sysroot: bool, 149 with_sysroot: bool,
138 ) -> Result<ProjectWorkspace> { 150 ) -> Result<ProjectWorkspace> {
139 let res = match root { 151 let res = match root {
140 ProjectRoot::ProjectJson(project_json) => { 152 ProjectManifest::ProjectJson(project_json) => {
141 let file = File::open(&project_json).with_context(|| { 153 let file = File::open(&project_json).with_context(|| {
142 format!("Failed to open json file {}", project_json.display()) 154 format!("Failed to open json file {}", project_json.display())
143 })?; 155 })?;
@@ -148,7 +160,7 @@ impl ProjectWorkspace {
148 })?, 160 })?,
149 } 161 }
150 } 162 }
151 ProjectRoot::CargoToml(cargo_toml) => { 163 ProjectManifest::CargoToml(cargo_toml) => {
152 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 164 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
153 .with_context(|| { 165 .with_context(|| {
154 format!( 166 format!(
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index e82fd57de..8d071ab1c 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -4,9 +4,14 @@
4mod args; 4mod args;
5 5
6use lsp_server::Connection; 6use lsp_server::Connection;
7use rust_analyzer::{cli, config::Config, from_json, Result}; 7use rust_analyzer::{
8 cli,
9 config::{Config, LinkedProject},
10 from_json, Result,
11};
8 12
9use crate::args::HelpPrinted; 13use crate::args::HelpPrinted;
14use ra_project_model::ProjectManifest;
10 15
11fn main() -> Result<()> { 16fn main() -> Result<()> {
12 setup_logging()?; 17 setup_logging()?;
@@ -97,17 +102,6 @@ fn run_server() -> Result<()> {
97 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default()); 102 log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
98 } 103 }
99 104
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
111 let config = { 105 let config = {
112 let mut config = Config::default(); 106 let mut config = Config::default();
113 if let Some(value) = &initialize_params.initialization_options { 107 if let Some(value) = &initialize_params.initialization_options {
@@ -115,10 +109,31 @@ fn run_server() -> Result<()> {
115 } 109 }
116 config.update_caps(&initialize_params.capabilities); 110 config.update_caps(&initialize_params.capabilities);
117 111
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
118 config 133 config
119 }; 134 };
120 135
121 rust_analyzer::main_loop(workspace_roots, config, connection)?; 136 rust_analyzer::main_loop(config, connection)?;
122 137
123 log::info!("shutting down IO..."); 138 log::info!("shutting down IO...");
124 io_threads.join()?; 139 io_threads.join()?;
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 8eaf75ff6..c7e86fe0c 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -8,7 +8,8 @@ use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{ExternSourceId, FileId, SourceRootId}; 8use ra_db::{ExternSourceId, FileId, SourceRootId};
9use ra_ide::{AnalysisChange, AnalysisHost}; 9use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{ 10use ra_project_model::{
11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace, 11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectManifest,
12 ProjectWorkspace,
12}; 13};
13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 14use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
14use rustc_hash::{FxHashMap, FxHashSet}; 15use rustc_hash::{FxHashMap, FxHashSet};
@@ -28,7 +29,7 @@ pub fn load_cargo(
28 with_proc_macro: bool, 29 with_proc_macro: bool,
29) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { 30) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
30 let root = std::env::current_dir()?.join(root); 31 let root = std::env::current_dir()?.join(root);
31 let root = ProjectRoot::discover_single(&root)?; 32 let root = ProjectManifest::discover_single(&root)?;
32 let ws = ProjectWorkspace::load( 33 let ws = ProjectWorkspace::load(
33 root, 34 root,
34 &CargoConfig { load_out_dirs_from_check, ..Default::default() }, 35 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 9c6e369d2..761bc9c2d 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -12,14 +12,13 @@ use std::{ffi::OsString, path::PathBuf};
12use lsp_types::ClientCapabilities; 12use lsp_types::ClientCapabilities;
13use ra_flycheck::FlycheckConfig; 13use ra_flycheck::FlycheckConfig;
14use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; 14use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig};
15use ra_project_model::CargoConfig; 15use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
16use serde::Deserialize; 16use serde::Deserialize;
17 17
18#[derive(Debug, Clone)] 18#[derive(Debug, Clone)]
19pub struct Config { 19pub struct Config {
20 pub client_caps: ClientCapsConfig, 20 pub client_caps: ClientCapsConfig,
21 21
22 pub with_sysroot: bool,
23 pub publish_diagnostics: bool, 22 pub publish_diagnostics: bool,
24 pub lru_capacity: Option<usize>, 23 pub lru_capacity: Option<usize>,
25 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>, 24 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
@@ -35,6 +34,27 @@ pub struct Config {
35 pub assist: AssistConfig, 34 pub assist: AssistConfig,
36 pub call_info_full: bool, 35 pub call_info_full: bool,
37 pub lens: LensConfig, 36 pub lens: LensConfig,
37
38 pub with_sysroot: bool,
39 pub linked_projects: Vec<LinkedProject>,
40}
41
42#[derive(Debug, Clone)]
43pub enum LinkedProject {
44 ProjectManifest(ProjectManifest),
45 JsonProject(JsonProject),
46}
47
48impl From<ProjectManifest> for LinkedProject {
49 fn from(v: ProjectManifest) -> Self {
50 LinkedProject::ProjectManifest(v)
51 }
52}
53
54impl From<JsonProject> for LinkedProject {
55 fn from(v: JsonProject) -> Self {
56 LinkedProject::JsonProject(v)
57 }
38} 58}
39 59
40#[derive(Clone, Debug, PartialEq, Eq)] 60#[derive(Clone, Debug, PartialEq, Eq)]
@@ -141,6 +161,7 @@ impl Default for Config {
141 assist: AssistConfig::default(), 161 assist: AssistConfig::default(),
142 call_info_full: true, 162 call_info_full: true,
143 lens: LensConfig::default(), 163 lens: LensConfig::default(),
164 linked_projects: Vec::new(),
144 } 165 }
145 } 166 }
146} 167}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 35f2d7001..d901f21d7 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -12,13 +12,11 @@ use std::{
12 fmt, 12 fmt,
13 ops::Range, 13 ops::Range,
14 panic, 14 panic,
15 path::PathBuf,
16 sync::Arc, 15 sync::Arc,
17 time::{Duration, Instant}, 16 time::{Duration, Instant},
18}; 17};
19 18
20use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 19use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
21use itertools::Itertools;
22use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 20use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
23use lsp_types::{ 21use lsp_types::{
24 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, 22 DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress,
@@ -36,7 +34,7 @@ use serde::{de::DeserializeOwned, Serialize};
36use threadpool::ThreadPool; 34use threadpool::ThreadPool;
37 35
38use crate::{ 36use crate::{
39 config::{Config, FilesWatcher}, 37 config::{Config, FilesWatcher, LinkedProject},
40 diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, 38 diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask},
41 from_proto, 39 from_proto,
42 global_state::{GlobalState, GlobalStateSnapshot}, 40 global_state::{GlobalState, GlobalStateSnapshot},
@@ -70,7 +68,7 @@ impl fmt::Display for LspError {
70 68
71impl Error for LspError {} 69impl Error for LspError {}
72 70
73pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> { 71pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
74 log::info!("initial config: {:#?}", config); 72 log::info!("initial config: {:#?}", config);
75 73
76 // Windows scheduler implements priority boosts: if thread waits for an 74 // 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)
95 let mut loop_state = LoopState::default(); 93 let mut loop_state = LoopState::default();
96 let mut global_state = { 94 let mut global_state = {
97 let workspaces = { 95 let workspaces = {
98 // FIXME: support dynamic workspace loading. 96 if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found {
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 {
106 show_message( 97 show_message(
107 lsp_types::MessageType::Error, 98 lsp_types::MessageType::Error,
108 format!( 99 "rust-analyzer failed to discover workspace".to_string(),
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 ),
112 &connection.sender, 100 &connection.sender,
113 ); 101 );
114 }; 102 };
115 103
116 project_roots 104 config
117 .into_iter() 105 .linked_projects
106 .iter()
107 .filter_map(|project| match project {
108 LinkedProject::ProjectManifest(it) => Some(it),
109 LinkedProject::JsonProject(_) => None,
110 })
118 .filter_map(|root| { 111 .filter_map(|root| {
119 ra_project_model::ProjectWorkspace::load( 112 ra_project_model::ProjectWorkspace::load(
120 root, 113 root.clone(),
121 &config.cargo, 114 &config.cargo,
122 config.with_sysroot, 115 config.with_sysroot,
123 ) 116 )
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 66a6f4d54..30d03b622 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -19,8 +19,9 @@ use serde_json::{to_string_pretty, Value};
19use tempfile::TempDir; 19use tempfile::TempDir;
20use test_utils::{find_mismatch, parse_fixture}; 20use test_utils::{find_mismatch, parse_fixture};
21 21
22use ra_project_model::ProjectManifest;
22use rust_analyzer::{ 23use rust_analyzer::{
23 config::{ClientCapsConfig, Config}, 24 config::{ClientCapsConfig, Config, LinkedProject},
24 main_loop, 25 main_loop,
25}; 26};
26 27
@@ -42,7 +43,7 @@ impl<'a> Project<'a> {
42 self 43 self
43 } 44 }
44 45
45 pub fn root(mut self, path: &str) -> Project<'a> { 46 pub(crate) fn root(mut self, path: &str) -> Project<'a> {
46 self.roots.push(path.into()); 47 self.roots.push(path.into());
47 self 48 self
48 } 49 }
@@ -74,7 +75,16 @@ impl<'a> Project<'a> {
74 paths.push((path, entry.text)); 75 paths.push((path, entry.text));
75 } 76 }
76 77
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<_>>();
78 88
79 let mut config = Config { 89 let mut config = Config {
80 client_caps: ClientCapsConfig { 90 client_caps: ClientCapsConfig {
@@ -84,6 +94,7 @@ impl<'a> Project<'a> {
84 ..Default::default() 94 ..Default::default()
85 }, 95 },
86 with_sysroot: self.with_sysroot, 96 with_sysroot: self.with_sysroot,
97 linked_projects,
87 ..Config::default() 98 ..Config::default()
88 }; 99 };
89 100
@@ -91,7 +102,7 @@ impl<'a> Project<'a> {
91 f(&mut config) 102 f(&mut config)
92 } 103 }
93 104
94 Server::new(tmp_dir, config, roots, paths) 105 Server::new(tmp_dir, config, paths)
95 } 106 }
96} 107}
97 108
@@ -109,20 +120,12 @@ pub struct Server {
109} 120}
110 121
111impl Server { 122impl Server {
112 fn new( 123 fn new(dir: TempDir, config: Config, files: Vec<(PathBuf, String)>) -> Server {
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 };
121 let (connection, client) = Connection::memory(); 124 let (connection, client) = Connection::memory();
122 125
123 let _thread = jod_thread::Builder::new() 126 let _thread = jod_thread::Builder::new()
124 .name("test server".to_string()) 127 .name("test server".to_string())
125 .spawn(move || main_loop(roots, config, connection).unwrap()) 128 .spawn(move || main_loop(config, connection).unwrap())
126 .expect("failed to spawn a thread"); 129 .expect("failed to spawn a thread");
127 130
128 let res = 131 let res =