aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_project_model/src/lib.rs78
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs36
3 files changed, 41 insertions, 75 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index df4be94dc..03f2629da 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -5,9 +5,8 @@ mod json_project;
5mod sysroot; 5mod sysroot;
6 6
7use std::{ 7use std::{
8 error::Error,
9 fs::{read_dir, File, ReadDir}, 8 fs::{read_dir, File, ReadDir},
10 io::BufReader, 9 io::{self, BufReader},
11 path::{Path, PathBuf}, 10 path::{Path, PathBuf},
12 process::Command, 11 process::Command,
13}; 12};
@@ -25,25 +24,6 @@ pub use crate::{
25}; 24};
26pub use ra_proc_macro::ProcMacroClient; 25pub use ra_proc_macro::ProcMacroClient;
27 26
28#[derive(Clone, PartialEq, Eq, Hash, Debug)]
29pub struct CargoTomlNotFoundError {
30 pub searched_at: PathBuf,
31 pub reason: String,
32}
33
34impl std::fmt::Display for CargoTomlNotFoundError {
35 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 write!(
37 fmt,
38 "can't find Cargo.toml at {}, due to {}",
39 self.searched_at.display(),
40 self.reason
41 )
42 }
43}
44
45impl Error for CargoTomlNotFoundError {}
46
47#[derive(Debug, Clone)] 27#[derive(Debug, Clone)]
48pub enum ProjectWorkspace { 28pub enum ProjectWorkspace {
49 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
@@ -94,11 +74,25 @@ impl ProjectRoot {
94 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())
95 } 75 }
96 76
97 pub fn discover(path: &Path) -> Result<ProjectRoot, CargoTomlNotFoundError> { 77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> {
78 let mut candidates = ProjectRoot::discover(path)?;
79 let res = match candidates.pop() {
80 None => bail!("no projects"),
81 Some(it) => it,
82 };
83
84 if !candidates.is_empty() {
85 bail!("more than one project")
86 }
87 Ok(res)
88 }
89
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> {
98 if let Some(project_json) = find_rust_project_json(path) { 91 if let Some(project_json) = find_rust_project_json(path) {
99 return Ok(ProjectRoot::ProjectJson(project_json)); 92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]);
100 } 93 }
101 return find_cargo_toml(path).map(ProjectRoot::CargoToml); 94 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect());
102 96
103 fn find_rust_project_json(path: &Path) -> Option<PathBuf> { 97 fn find_rust_project_json(path: &Path) -> Option<PathBuf> {
104 if path.ends_with("rust-project.json") { 98 if path.ends_with("rust-project.json") {
@@ -117,43 +111,17 @@ impl ProjectRoot {
117 None 111 None
118 } 112 }
119 113
120 fn find_cargo_toml(path: &Path) -> Result<PathBuf, CargoTomlNotFoundError> { 114 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
121 if path.ends_with("Cargo.toml") { 115 if path.ends_with("Cargo.toml") {
122 return Ok(path.to_path_buf()); 116 return Ok(vec![path.to_path_buf()]);
123 } 117 }
124 118
125 if let Some(p) = find_cargo_toml_in_parent_dir(path) { 119 if let Some(p) = find_cargo_toml_in_parent_dir(path) {
126 return Ok(p); 120 return Ok(vec![p]);
127 } 121 }
128 122
129 let entities = match read_dir(path) { 123 let entities = read_dir(path)?;
130 Ok(entities) => entities, 124 Ok(find_cargo_toml_in_child_dir(entities))
131 Err(e) => {
132 return Err(CargoTomlNotFoundError {
133 searched_at: path.to_path_buf(),
134 reason: format!("file system error: {}", e),
135 }
136 .into());
137 }
138 };
139
140 let mut valid_canditates = find_cargo_toml_in_child_dir(entities);
141 return match valid_canditates.len() {
142 1 => Ok(valid_canditates.remove(0)),
143 0 => Err(CargoTomlNotFoundError {
144 searched_at: path.to_path_buf(),
145 reason: "no Cargo.toml file found".to_string(),
146 }
147 .into()),
148 _ => Err(CargoTomlNotFoundError {
149 searched_at: path.to_path_buf(),
150 reason: format!(
151 "multiple equally valid Cargo.toml files found: {:?}",
152 valid_canditates
153 ),
154 }
155 .into()),
156 };
157 } 125 }
158 126
159 fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> { 127 fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> {
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index f3f266ee4..e620712ad 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -27,7 +27,7 @@ pub(crate) fn load_cargo(
27 load_out_dirs_from_check: bool, 27 load_out_dirs_from_check: bool,
28) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { 28) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
29 let root = std::env::current_dir()?.join(root); 29 let root = std::env::current_dir()?.join(root);
30 let root = ProjectRoot::discover(&root)?; 30 let root = ProjectRoot::discover_single(&root)?;
31 let ws = ProjectWorkspace::load( 31 let ws = ProjectWorkspace::load(
32 root, 32 root,
33 &CargoConfig { load_out_dirs_from_check, ..Default::default() }, 33 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 9c345601c..fc4c77f8a 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -15,6 +15,7 @@ use std::{
15}; 15};
16 16
17use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 17use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
18use itertools::Itertools;
18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 19use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
19use lsp_types::{ 20use lsp_types::{
20 NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, 21 NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams,
@@ -93,27 +94,24 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
93 let mut visited = FxHashSet::default(); 94 let mut visited = FxHashSet::default();
94 let project_roots = ws_roots 95 let project_roots = ws_roots
95 .iter() 96 .iter()
96 .map(|it| ra_project_model::ProjectRoot::discover(it)) 97 .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
97 .filter_map(|dir| { 98 .flatten()
98 dir.map_err(|cargo_toml_not_found| { 99 .filter(|it| visited.insert(it.clone()))
99 log::error!("discovering workspace failed: {:?}", cargo_toml_not_found); 100 .collect::<Vec<_>>();
100 101
101 if config.notifications.cargo_toml_not_found { 102 if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
102 show_message( 103 show_message(
103 req::MessageType::Error, 104 req::MessageType::Error,
104 format!( 105 format!(
105 "rust-analyzer failed to discover workspace: {:?}", 106 "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
106 cargo_toml_not_found 107 ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
107 ), 108 ),
108 &connection.sender, 109 &connection.sender,
109 ); 110 );
110 } 111 };
111 })
112 .ok()
113 })
114 .filter(|it| visited.insert(it.clone()));
115 112
116 project_roots 113 project_roots
114 .into_iter()
117 .filter_map(|root| { 115 .filter_map(|root| {
118 ra_project_model::ProjectWorkspace::load( 116 ra_project_model::ProjectWorkspace::load(
119 root, 117 root,