aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/flycheck/src/lib.rs92
-rw-r--r--crates/ra_assists/src/handlers/change_return_type_to_result.rs33
-rw-r--r--crates/ra_hir_def/src/body/lower.rs98
-rw-r--r--crates/ra_hir_def/src/body/scope.rs13
-rw-r--r--crates/ra_hir_def/src/nameres/tests/mod_resolution.rs16
-rw-r--r--crates/rust-analyzer/src/global_state.rs52
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs85
-rw-r--r--crates/rust-analyzer/src/main_loop.rs115
-rw-r--r--crates/rust-analyzer/src/reload.rs44
-rw-r--r--crates/vfs/src/file_set.rs2
-rw-r--r--crates/vfs/src/vfs_path.rs16
12 files changed, 293 insertions, 275 deletions
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs
index 4dcab7a61..92ec4f92e 100644
--- a/crates/flycheck/src/lib.rs
+++ b/crates/flycheck/src/lib.rs
@@ -7,7 +7,7 @@ use std::{
7 io::{self, BufReader}, 7 io::{self, BufReader},
8 path::PathBuf, 8 path::PathBuf,
9 process::{Command, Stdio}, 9 process::{Command, Stdio},
10 time::Instant, 10 time::Duration,
11}; 11};
12 12
13use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; 13use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
@@ -74,9 +74,6 @@ impl FlycheckHandle {
74 74
75#[derive(Debug)] 75#[derive(Debug)]
76pub enum Message { 76pub enum Message {
77 /// Request a clearing of all cached diagnostics from the check watcher
78 ClearDiagnostics,
79
80 /// Request adding a diagnostic with fixes included to a file 77 /// Request adding a diagnostic with fixes included to a file
81 AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, 78 AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic },
82 79
@@ -86,9 +83,10 @@ pub enum Message {
86 83
87#[derive(Debug)] 84#[derive(Debug)]
88pub enum Progress { 85pub enum Progress {
89 Being, 86 DidStart,
90 DidCheckCrate(String), 87 DidCheckCrate(String),
91 End, 88 DidFinish,
89 DidCancel,
92} 90}
93 91
94struct Restart; 92struct Restart;
@@ -97,19 +95,18 @@ struct FlycheckActor {
97 sender: Box<dyn Fn(Message) + Send>, 95 sender: Box<dyn Fn(Message) + Send>,
98 config: FlycheckConfig, 96 config: FlycheckConfig,
99 workspace_root: PathBuf, 97 workspace_root: PathBuf,
100 last_update_req: Option<Instant>,
101 /// WatchThread exists to wrap around the communication needed to be able to 98 /// WatchThread exists to wrap around the communication needed to be able to
102 /// run `cargo check` without blocking. Currently the Rust standard library 99 /// run `cargo check` without blocking. Currently the Rust standard library
103 /// doesn't provide a way to read sub-process output without blocking, so we 100 /// doesn't provide a way to read sub-process output without blocking, so we
104 /// have to wrap sub-processes output handling in a thread and pass messages 101 /// have to wrap sub-processes output handling in a thread and pass messages
105 /// back over a channel. 102 /// back over a channel.
106 // XXX: drop order is significant 103 // XXX: drop order is significant
107 check_process: Option<(Receiver<CheckEvent>, jod_thread::JoinHandle)>, 104 check_process: Option<(Receiver<cargo_metadata::Message>, jod_thread::JoinHandle)>,
108} 105}
109 106
110enum Event { 107enum Event {
111 Restart(Restart), 108 Restart(Restart),
112 CheckEvent(Option<CheckEvent>), 109 CheckEvent(Option<cargo_metadata::Message>),
113} 110}
114 111
115impl FlycheckActor { 112impl FlycheckActor {
@@ -118,7 +115,7 @@ impl FlycheckActor {
118 config: FlycheckConfig, 115 config: FlycheckConfig,
119 workspace_root: PathBuf, 116 workspace_root: PathBuf,
120 ) -> FlycheckActor { 117 ) -> FlycheckActor {
121 FlycheckActor { sender, config, workspace_root, last_update_req: None, check_process: None } 118 FlycheckActor { sender, config, workspace_root, check_process: None }
122 } 119 }
123 fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { 120 fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
124 let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan); 121 let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan);
@@ -128,65 +125,48 @@ impl FlycheckActor {
128 } 125 }
129 } 126 }
130 fn run(&mut self, inbox: Receiver<Restart>) { 127 fn run(&mut self, inbox: Receiver<Restart>) {
131 // If we rerun the thread, we need to discard the previous check results first
132 self.send(Message::ClearDiagnostics);
133 self.send(Message::Progress(Progress::End));
134
135 while let Some(event) = self.next_event(&inbox) { 128 while let Some(event) = self.next_event(&inbox) {
136 match event { 129 match event {
137 Event::Restart(Restart) => self.last_update_req = Some(Instant::now()), 130 Event::Restart(Restart) => {
131 while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {}
132 self.cancel_check_process();
133 self.check_process = Some(self.start_check_process());
134 self.send(Message::Progress(Progress::DidStart));
135 }
138 Event::CheckEvent(None) => { 136 Event::CheckEvent(None) => {
139 // Watcher finished, replace it with a never channel to 137 // Watcher finished, replace it with a never channel to
140 // avoid busy-waiting. 138 // avoid busy-waiting.
141 self.check_process = None; 139 assert!(self.check_process.take().is_some());
140 self.send(Message::Progress(Progress::DidFinish));
142 } 141 }
143 Event::CheckEvent(Some(event)) => match event { 142 Event::CheckEvent(Some(message)) => match message {
144 CheckEvent::Begin => { 143 cargo_metadata::Message::CompilerArtifact(msg) => {
145 self.send(Message::Progress(Progress::Being));
146 }
147
148 CheckEvent::End => {
149 self.send(Message::Progress(Progress::End));
150 }
151
152 CheckEvent::Msg(cargo_metadata::Message::CompilerArtifact(msg)) => {
153 self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); 144 self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name)));
154 } 145 }
155 146
156 CheckEvent::Msg(cargo_metadata::Message::CompilerMessage(msg)) => { 147 cargo_metadata::Message::CompilerMessage(msg) => {
157 self.send(Message::AddDiagnostic { 148 self.send(Message::AddDiagnostic {
158 workspace_root: self.workspace_root.clone(), 149 workspace_root: self.workspace_root.clone(),
159 diagnostic: msg.message, 150 diagnostic: msg.message,
160 }); 151 });
161 } 152 }
162 153
163 CheckEvent::Msg(cargo_metadata::Message::BuildScriptExecuted(_)) 154 cargo_metadata::Message::BuildScriptExecuted(_)
164 | CheckEvent::Msg(cargo_metadata::Message::BuildFinished(_)) 155 | cargo_metadata::Message::BuildFinished(_)
165 | CheckEvent::Msg(cargo_metadata::Message::TextLine(_)) 156 | cargo_metadata::Message::TextLine(_)
166 | CheckEvent::Msg(cargo_metadata::Message::Unknown) => {} 157 | cargo_metadata::Message::Unknown => {}
167 }, 158 },
168 } 159 }
169 if self.should_recheck() {
170 self.last_update_req = None;
171 self.send(Message::ClearDiagnostics);
172 self.restart_check_process();
173 }
174 } 160 }
161 // If we rerun the thread, we need to discard the previous check results first
162 self.cancel_check_process();
175 } 163 }
176 fn should_recheck(&mut self) -> bool { 164 fn cancel_check_process(&mut self) {
177 if let Some(_last_update_req) = &self.last_update_req { 165 if self.check_process.take().is_some() {
178 // We currently only request an update on save, as we need up to 166 self.send(Message::Progress(Progress::DidCancel));
179 // date source on disk for cargo check to do it's magic, so we
180 // don't really need to debounce the requests at this point.
181 return true;
182 } 167 }
183 false
184 } 168 }
185 169 fn start_check_process(&self) -> (Receiver<cargo_metadata::Message>, jod_thread::JoinHandle) {
186 fn restart_check_process(&mut self) {
187 // First, clear and cancel the old thread
188 self.check_process = None;
189
190 let mut cmd = match &self.config { 170 let mut cmd = match &self.config {
191 FlycheckConfig::CargoCommand { 171 FlycheckConfig::CargoCommand {
192 command, 172 command,
@@ -223,8 +203,6 @@ impl FlycheckActor {
223 let thread = jod_thread::spawn(move || { 203 let thread = jod_thread::spawn(move || {
224 // If we trigger an error here, we will do so in the loop instead, 204 // If we trigger an error here, we will do so in the loop instead,
225 // which will break out of the loop, and continue the shutdown 205 // which will break out of the loop, and continue the shutdown
226 let _ = message_send.send(CheckEvent::Begin);
227
228 let res = run_cargo(cmd, &mut |message| { 206 let res = run_cargo(cmd, &mut |message| {
229 // Skip certain kinds of messages to only spend time on what's useful 207 // Skip certain kinds of messages to only spend time on what's useful
230 match &message { 208 match &message {
@@ -237,7 +215,7 @@ impl FlycheckActor {
237 } 215 }
238 216
239 // if the send channel was closed, we want to shutdown 217 // if the send channel was closed, we want to shutdown
240 message_send.send(CheckEvent::Msg(message)).is_ok() 218 message_send.send(message).is_ok()
241 }); 219 });
242 220
243 if let Err(err) = res { 221 if let Err(err) = res {
@@ -245,12 +223,8 @@ impl FlycheckActor {
245 // to display user-caused misconfiguration errors instead of just logging them here 223 // to display user-caused misconfiguration errors instead of just logging them here
246 log::error!("Cargo watcher failed {:?}", err); 224 log::error!("Cargo watcher failed {:?}", err);
247 } 225 }
248
249 // We can ignore any error here, as we are already in the progress
250 // of shutting down.
251 let _ = message_send.send(CheckEvent::End);
252 }); 226 });
253 self.check_process = Some((message_recv, thread)) 227 (message_recv, thread)
254 } 228 }
255 229
256 fn send(&self, check_task: Message) { 230 fn send(&self, check_task: Message) {
@@ -258,12 +232,6 @@ impl FlycheckActor {
258 } 232 }
259} 233}
260 234
261enum CheckEvent {
262 Begin,
263 Msg(cargo_metadata::Message),
264 End,
265}
266
267fn run_cargo( 235fn run_cargo(
268 mut command: Command, 236 mut command: Command,
269 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, 237 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
index c6baa0a57..855baf187 100644
--- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs
+++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
@@ -4,6 +4,7 @@ use ra_syntax::{
4}; 4};
5 5
6use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, Assists};
7use test_utils::mark;
7 8
8// Assist: change_return_type_to_result 9// Assist: change_return_type_to_result
9// 10//
@@ -22,8 +23,13 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex
22 let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?; 23 let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?;
23 24
24 let type_ref = &ret_type.type_ref()?; 25 let type_ref = &ret_type.type_ref()?;
25 if type_ref.syntax().text().to_string().starts_with("Result<") { 26 let ret_type_str = type_ref.syntax().text().to_string();
26 return None; 27 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
28 if let Some(ret_type_first_part) = first_part_ret_type {
29 if ret_type_first_part.ends_with("Result") {
30 mark::hit!(change_return_type_to_result_simple_return_type_already_result);
31 return None;
32 }
27 } 33 }
28 34
29 let block_expr = &fn_def.body()?; 35 let block_expr = &fn_def.body()?;
@@ -297,6 +303,29 @@ mod tests {
297 } 303 }
298 304
299 #[test] 305 #[test]
306 fn change_return_type_to_result_simple_return_type_already_result_std() {
307 check_assist_not_applicable(
308 change_return_type_to_result,
309 r#"fn foo() -> std::result::Result<i32<|>, String> {
310 let test = "test";
311 return 42i32;
312 }"#,
313 );
314 }
315
316 #[test]
317 fn change_return_type_to_result_simple_return_type_already_result() {
318 mark::check!(change_return_type_to_result_simple_return_type_already_result);
319 check_assist_not_applicable(
320 change_return_type_to_result,
321 r#"fn foo() -> Result<i32<|>, String> {
322 let test = "test";
323 return 42i32;
324 }"#,
325 );
326 }
327
328 #[test]
300 fn change_return_type_to_result_simple_with_cursor() { 329 fn change_return_type_to_result_simple_with_cursor() {
301 check_assist( 330 check_assist(
302 change_return_type_to_result, 331 change_return_type_to_result,
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 3ced648e5..a7e2e0982 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -5,7 +5,7 @@ use either::Either;
5use hir_expand::{ 5use hir_expand::{
6 hygiene::Hygiene, 6 hygiene::Hygiene,
7 name::{name, AsName, Name}, 7 name::{name, AsName, Name},
8 AstId, HirFileId, MacroDefId, MacroDefKind, 8 HirFileId, MacroDefId, MacroDefKind,
9}; 9};
10use ra_arena::Arena; 10use ra_arena::Arena;
11use ra_syntax::{ 11use ra_syntax::{
@@ -27,7 +27,7 @@ use crate::{
27 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, 27 LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
28 }, 28 },
29 item_scope::BuiltinShadowMode, 29 item_scope::BuiltinShadowMode,
30 item_tree::{FileItemTreeId, ItemTree, ItemTreeNode}, 30 item_tree::{ItemTree, ItemTreeId, ItemTreeNode},
31 path::{GenericArgs, Path}, 31 path::{GenericArgs, Path},
32 type_ref::{Mutability, Rawness, TypeRef}, 32 type_ref::{Mutability, Rawness, TypeRef},
33 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, 33 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
@@ -37,7 +37,7 @@ use crate::{
37use super::{ExprSource, PatSource}; 37use super::{ExprSource, PatSource};
38use ast::AstChildren; 38use ast::AstChildren;
39use rustc_hash::FxHashMap; 39use rustc_hash::FxHashMap;
40use std::sync::Arc; 40use std::{any::type_name, sync::Arc};
41 41
42pub(crate) struct LowerCtx { 42pub(crate) struct LowerCtx {
43 hygiene: Hygiene, 43 hygiene: Hygiene,
@@ -561,17 +561,30 @@ impl ExprCollector<'_> {
561 } 561 }
562 } 562 }
563 563
564 fn find_inner_item<S: ItemTreeNode>(&self, id: AstId<ast::ModuleItem>) -> FileItemTreeId<S> { 564 fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> {
565 let id = self.expander.ast_id(ast);
565 let tree = &self.item_trees[&id.file_id]; 566 let tree = &self.item_trees[&id.file_id];
566 567
567 // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes 568 // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
568 569
569 // Root file (non-macro). 570 // Root file (non-macro).
570 tree.all_inner_items() 571 let item_tree_id = tree
572 .all_inner_items()
571 .chain(tree.top_level_items().iter().copied()) 573 .chain(tree.top_level_items().iter().copied())
572 .filter_map(|mod_item| mod_item.downcast::<S>()) 574 .filter_map(|mod_item| mod_item.downcast::<N>())
573 .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value) 575 .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast())
574 .unwrap_or_else(|| panic!("couldn't find inner item for {:?}", id)) 576 .or_else(|| {
577 log::debug!(
578 "couldn't find inner {} item for {:?} (AST: `{}` - {:?})",
579 type_name::<N>(),
580 id,
581 ast.syntax(),
582 ast.syntax(),
583 );
584 None
585 })?;
586
587 Some(ItemTreeId::new(id.file_id, item_tree_id))
575 } 588 }
576 589
577 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { 590 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
@@ -611,82 +624,45 @@ impl ExprCollector<'_> {
611 .filter_map(|item| { 624 .filter_map(|item| {
612 let (def, name): (ModuleDefId, Option<ast::Name>) = match item { 625 let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
613 ast::ModuleItem::FnDef(def) => { 626 ast::ModuleItem::FnDef(def) => {
614 let ast_id = self.expander.ast_id(&def); 627 let id = self.find_inner_item(&def)?;
615 let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
616 ( 628 (
617 FunctionLoc { container: container.into(), id: ast_id.with_value(id) } 629 FunctionLoc { container: container.into(), id }.intern(self.db).into(),
618 .intern(self.db)
619 .into(),
620 def.name(), 630 def.name(),
621 ) 631 )
622 } 632 }
623 ast::ModuleItem::TypeAliasDef(def) => { 633 ast::ModuleItem::TypeAliasDef(def) => {
624 let ast_id = self.expander.ast_id(&def); 634 let id = self.find_inner_item(&def)?;
625 let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
626 ( 635 (
627 TypeAliasLoc { container: container.into(), id: ast_id.with_value(id) } 636 TypeAliasLoc { container: container.into(), id }.intern(self.db).into(),
628 .intern(self.db)
629 .into(),
630 def.name(), 637 def.name(),
631 ) 638 )
632 } 639 }
633 ast::ModuleItem::ConstDef(def) => { 640 ast::ModuleItem::ConstDef(def) => {
634 let ast_id = self.expander.ast_id(&def); 641 let id = self.find_inner_item(&def)?;
635 let id = self.find_inner_item(ast_id.map(|id| id.upcast()));
636 ( 642 (
637 ConstLoc { container: container.into(), id: ast_id.with_value(id) } 643 ConstLoc { container: container.into(), id }.intern(self.db).into(),
638 .intern(self.db)
639 .into(),
640 def.name(), 644 def.name(),
641 ) 645 )
642 } 646 }
643 ast::ModuleItem::StaticDef(def) => { 647 ast::ModuleItem::StaticDef(def) => {
644 let ast_id = self.expander.ast_id(&def); 648 let id = self.find_inner_item(&def)?;
645 let id = self.find_inner_item(ast_id.map(|id| id.upcast())); 649 (StaticLoc { container, id }.intern(self.db).into(), def.name())
646 (
647 StaticLoc { container, id: ast_id.with_value(id) }
648 .intern(self.db)
649 .into(),
650 def.name(),
651 )
652 } 650 }
653 ast::ModuleItem::StructDef(def) => { 651 ast::ModuleItem::StructDef(def) => {
654 let ast_id = self.expander.ast_id(&def); 652 let id = self.find_inner_item(&def)?;
655 let id = self.find_inner_item(ast_id.map(|id| id.upcast())); 653 (StructLoc { container, id }.intern(self.db).into(), def.name())
656 (
657 StructLoc { container, id: ast_id.with_value(id) }
658 .intern(self.db)
659 .into(),
660 def.name(),
661 )
662 } 654 }
663 ast::ModuleItem::EnumDef(def) => { 655 ast::ModuleItem::EnumDef(def) => {
664 let ast_id = self.expander.ast_id(&def); 656 let id = self.find_inner_item(&def)?;
665 let id = self.find_inner_item(ast_id.map(|id| id.upcast())); 657 (EnumLoc { container, id }.intern(self.db).into(), def.name())
666 (
667 EnumLoc { container, id: ast_id.with_value(id) }.intern(self.db).into(),
668 def.name(),
669 )
670 } 658 }
671 ast::ModuleItem::UnionDef(def) => { 659 ast::ModuleItem::UnionDef(def) => {
672 let ast_id = self.expander.ast_id(&def); 660 let id = self.find_inner_item(&def)?;
673 let id = self.find_inner_item(ast_id.map(|id| id.upcast())); 661 (UnionLoc { container, id }.intern(self.db).into(), def.name())
674 (
675 UnionLoc { container, id: ast_id.with_value(id) }
676 .intern(self.db)
677 .into(),
678 def.name(),
679 )
680 } 662 }
681 ast::ModuleItem::TraitDef(def) => { 663 ast::ModuleItem::TraitDef(def) => {
682 let ast_id = self.expander.ast_id(&def); 664 let id = self.find_inner_item(&def)?;
683 let id = self.find_inner_item(ast_id.map(|id| id.upcast())); 665 (TraitLoc { container, id }.intern(self.db).into(), def.name())
684 (
685 TraitLoc { container, id: ast_id.with_value(id) }
686 .intern(self.db)
687 .into(),
688 def.name(),
689 )
690 } 666 }
691 ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks 667 ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks
692 ast::ModuleItem::ImplDef(_) 668 ast::ModuleItem::ImplDef(_)
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index 81397b063..99e876683 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -337,6 +337,19 @@ fn foo() {
337 ); 337 );
338 } 338 }
339 339
340 #[test]
341 fn broken_inner_item() {
342 do_check(
343 r"
344 fn foo() {
345 trait {}
346 <|>
347 }
348 ",
349 &[],
350 );
351 }
352
340 fn do_check_local_name(ra_fixture: &str, expected_offset: u32) { 353 fn do_check_local_name(ra_fixture: &str, expected_offset: u32) {
341 let (db, position) = TestDB::with_position(ra_fixture); 354 let (db, position) = TestDB::with_position(ra_fixture);
342 let file_id = position.file_id; 355 let file_id = position.file_id;
diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
index e9a5e4cba..753684201 100644
--- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
@@ -335,6 +335,22 @@ fn module_resolution_relative_path_2() {
335} 335}
336 336
337#[test] 337#[test]
338fn module_resolution_relative_path_outside_root() {
339 let map = def_map(
340 r###"
341 //- /main.rs
342
343 #[path="../../../../../outside.rs"]
344 mod foo;
345 "###,
346 );
347
348 assert_snapshot!(map, @r###"
349 â‹®crate
350 "###);
351}
352
353#[test]
338fn module_resolution_explicit_path_mod_rs_2() { 354fn module_resolution_explicit_path_mod_rs_2() {
339 let map = def_map( 355 let map = def_map(
340 r###" 356 r###"
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 4da094083..b8aa1e5b5 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -12,6 +12,7 @@ use parking_lot::RwLock;
12use ra_db::{CrateId, VfsPath}; 12use ra_db::{CrateId, VfsPath};
13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; 13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; 14use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target};
15use rustc_hash::{FxHashMap, FxHashSet};
15 16
16use crate::{ 17use crate::{
17 config::Config, 18 config::Config,
@@ -21,12 +22,10 @@ use crate::{
21 main_loop::Task, 22 main_loop::Task,
22 reload::SourceRootConfig, 23 reload::SourceRootConfig,
23 request_metrics::{LatestRequests, RequestMetrics}, 24 request_metrics::{LatestRequests, RequestMetrics},
24 show_message,
25 thread_pool::TaskPool, 25 thread_pool::TaskPool,
26 to_proto::url_from_abs_path, 26 to_proto::url_from_abs_path,
27 Result, 27 Result,
28}; 28};
29use rustc_hash::{FxHashMap, FxHashSet};
30 29
31#[derive(Eq, PartialEq)] 30#[derive(Eq, PartialEq)]
32pub(crate) enum Status { 31pub(crate) enum Status {
@@ -58,6 +57,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
58/// Note that this struct has more than on impl in various modules! 57/// Note that this struct has more than on impl in various modules!
59pub(crate) struct GlobalState { 58pub(crate) struct GlobalState {
60 sender: Sender<lsp_server::Message>, 59 sender: Sender<lsp_server::Message>,
60 req_queue: ReqQueue,
61 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, 61 pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
62 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, 62 pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
63 pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>, 63 pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>,
@@ -67,7 +67,6 @@ pub(crate) struct GlobalState {
67 pub(crate) mem_docs: FxHashSet<VfsPath>, 67 pub(crate) mem_docs: FxHashSet<VfsPath>,
68 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 68 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
69 pub(crate) status: Status, 69 pub(crate) status: Status,
70 pub(crate) req_queue: ReqQueue,
71 pub(crate) source_root_config: SourceRootConfig, 70 pub(crate) source_root_config: SourceRootConfig,
72 pub(crate) proc_macro_client: ProcMacroClient, 71 pub(crate) proc_macro_client: ProcMacroClient,
73 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 72 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
@@ -103,16 +102,16 @@ impl GlobalState {
103 let analysis_host = AnalysisHost::new(config.lru_capacity); 102 let analysis_host = AnalysisHost::new(config.lru_capacity);
104 GlobalState { 103 GlobalState {
105 sender, 104 sender,
105 req_queue: ReqQueue::default(),
106 task_pool, 106 task_pool,
107 loader, 107 loader,
108 flycheck: None,
108 config, 109 config,
109 analysis_host, 110 analysis_host,
110 flycheck: None,
111 diagnostics: Default::default(), 111 diagnostics: Default::default(),
112 mem_docs: FxHashSet::default(), 112 mem_docs: FxHashSet::default(),
113 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), 113 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
114 status: Status::default(), 114 status: Status::default(),
115 req_queue: ReqQueue::default(),
116 source_root_config: SourceRootConfig::default(), 115 source_root_config: SourceRootConfig::default(),
117 proc_macro_client: ProcMacroClient::dummy(), 116 proc_macro_client: ProcMacroClient::dummy(),
118 workspaces: Arc::new(Vec::new()), 117 workspaces: Arc::new(Vec::new()),
@@ -169,8 +168,35 @@ impl GlobalState {
169 } 168 }
170 } 169 }
171 170
172 pub(crate) fn send(&mut self, message: lsp_server::Message) { 171 pub(crate) fn send_request<R: lsp_types::request::Request>(
173 self.sender.send(message).unwrap() 172 &mut self,
173 params: R::Params,
174 handler: ReqHandler,
175 ) {
176 let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler);
177 self.send(request.into());
178 }
179 pub(crate) fn complete_request(&mut self, response: lsp_server::Response) {
180 let handler = self.req_queue.outgoing.complete(response.id.clone());
181 handler(self, response)
182 }
183
184 pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
185 &mut self,
186 params: N::Params,
187 ) {
188 let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
189 self.send(not.into());
190 }
191
192 pub(crate) fn register_request(
193 &mut self,
194 request: &lsp_server::Request,
195 request_received: Instant,
196 ) {
197 self.req_queue
198 .incoming
199 .register(request.id.clone(), (request.method.clone(), request_received));
174 } 200 }
175 pub(crate) fn respond(&mut self, response: lsp_server::Response) { 201 pub(crate) fn respond(&mut self, response: lsp_server::Response) {
176 if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { 202 if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
@@ -182,8 +208,14 @@ impl GlobalState {
182 self.send(response.into()); 208 self.send(response.into());
183 } 209 }
184 } 210 }
185 pub(crate) fn show_message(&self, typ: lsp_types::MessageType, message: String) { 211 pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
186 show_message(typ, message, &self.sender) 212 if let Some(response) = self.req_queue.incoming.cancel(request_id) {
213 self.send(response.into());
214 }
215 }
216
217 fn send(&mut self, message: lsp_server::Message) {
218 self.sender.send(message).unwrap()
187 } 219 }
188} 220}
189 221
@@ -209,7 +241,7 @@ impl GlobalStateSnapshot {
209 pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { 241 pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url {
210 let mut base = self.vfs.read().0.file_path(file_id); 242 let mut base = self.vfs.read().0.file_path(file_id);
211 base.pop(); 243 base.pop();
212 let path = base.join(path); 244 let path = base.join(path).unwrap();
213 let path = path.as_path().unwrap(); 245 let path = path.as_path().unwrap();
214 url_from_abs_path(&path) 246 url_from_abs_path(&path)
215 } 247 }
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index a24dfe58c..407944d85 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -39,7 +39,7 @@ pub mod config;
39use serde::de::DeserializeOwned; 39use serde::de::DeserializeOwned;
40 40
41pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; 41pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>;
42pub use crate::{caps::server_capabilities, lsp_utils::show_message, main_loop::main_loop}; 42pub use crate::{caps::server_capabilities, main_loop::main_loop};
43use std::fmt; 43use std::fmt;
44 44
45pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { 45pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> {
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs
index 35917030c..0bc3ff115 100644
--- a/crates/rust-analyzer/src/lsp_utils.rs
+++ b/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,24 +1,11 @@
1//! Utilities for LSP-related boilerplate code. 1//! Utilities for LSP-related boilerplate code.
2use std::{error::Error, ops::Range}; 2use std::{error::Error, ops::Range};
3 3
4use crossbeam_channel::Sender; 4use lsp_server::Notification;
5use lsp_server::{Message, Notification};
6use ra_db::Canceled; 5use ra_db::Canceled;
7use ra_ide::LineIndex; 6use ra_ide::LineIndex;
8use serde::Serialize;
9 7
10use crate::from_proto; 8use crate::{from_proto, global_state::GlobalState};
11
12pub fn show_message(
13 typ: lsp_types::MessageType,
14 message: impl Into<String>,
15 sender: &Sender<Message>,
16) {
17 let message = message.into();
18 let params = lsp_types::ShowMessageParams { typ, message };
19 let not = notification_new::<lsp_types::notification::ShowMessage>(params);
20 sender.send(not.into()).unwrap();
21}
22 9
23pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { 10pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool {
24 e.downcast_ref::<Canceled>().is_some() 11 e.downcast_ref::<Canceled>().is_some()
@@ -30,12 +17,68 @@ pub(crate) fn notification_is<N: lsp_types::notification::Notification>(
30 notification.method == N::METHOD 17 notification.method == N::METHOD
31} 18}
32 19
33pub(crate) fn notification_new<N>(params: N::Params) -> Notification 20#[derive(Debug, Eq, PartialEq)]
34where 21pub(crate) enum Progress {
35 N: lsp_types::notification::Notification, 22 Begin,
36 N::Params: Serialize, 23 Report,
37{ 24 End,
38 Notification::new(N::METHOD.to_string(), params) 25}
26
27impl Progress {
28 pub(crate) fn percentage(done: usize, total: usize) -> f64 {
29 (done as f64 / total.max(1) as f64) * 100.0
30 }
31}
32
33impl GlobalState {
34 pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) {
35 let message = message.into();
36 self.send_notification::<lsp_types::notification::ShowMessage>(
37 lsp_types::ShowMessageParams { typ, message },
38 )
39 }
40
41 pub(crate) fn report_progress(
42 &mut self,
43 title: &str,
44 state: Progress,
45 message: Option<String>,
46 percentage: Option<f64>,
47 ) {
48 if !self.config.client_caps.work_done_progress {
49 return;
50 }
51 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
52 let work_done_progress = match state {
53 Progress::Begin => {
54 self.send_request::<lsp_types::request::WorkDoneProgressCreate>(
55 lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
56 |_, _| (),
57 );
58
59 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
60 title: title.into(),
61 cancellable: None,
62 message,
63 percentage,
64 })
65 }
66 Progress::Report => {
67 lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
68 cancellable: None,
69 message,
70 percentage,
71 })
72 }
73 Progress::End => {
74 lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
75 }
76 };
77 self.send_notification::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
78 token,
79 value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
80 });
81 }
39} 82}
40 83
41pub(crate) fn apply_document_changes( 84pub(crate) fn apply_document_changes(
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 8fc816cbd..e5194fe41 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -7,7 +7,7 @@ use std::{
7 7
8use crossbeam_channel::{never, select, Receiver}; 8use crossbeam_channel::{never, select, Receiver};
9use lsp_server::{Connection, Notification, Request, Response}; 9use lsp_server::{Connection, Notification, Request, Response};
10use lsp_types::{notification::Notification as _, request::Request as _}; 10use lsp_types::notification::Notification as _;
11use ra_db::VfsPath; 11use ra_db::VfsPath;
12use ra_ide::{Canceled, FileId}; 12use ra_ide::{Canceled, FileId};
13use ra_prof::profile; 13use ra_prof::profile;
@@ -18,7 +18,7 @@ use crate::{
18 from_proto, 18 from_proto,
19 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, 19 global_state::{file_id_to_url, url_to_file_id, GlobalState, Status},
20 handlers, lsp_ext, 20 handlers, lsp_ext,
21 lsp_utils::{apply_document_changes, is_canceled, notification_is, notification_new}, 21 lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress},
22 Result, 22 Result,
23}; 23};
24 24
@@ -143,10 +143,7 @@ impl GlobalState {
143 lsp_server::Message::Notification(not) => { 143 lsp_server::Message::Notification(not) => {
144 self.on_notification(not)?; 144 self.on_notification(not)?;
145 } 145 }
146 lsp_server::Message::Response(resp) => { 146 lsp_server::Message::Response(resp) => self.complete_request(resp),
147 let handler = self.req_queue.outgoing.complete(resp.id.clone());
148 handler(self, resp)
149 }
150 }, 147 },
151 Event::Task(task) => { 148 Event::Task(task) => {
152 match task { 149 match task {
@@ -181,18 +178,15 @@ impl GlobalState {
181 became_ready = true; 178 became_ready = true;
182 Progress::End 179 Progress::End
183 }; 180 };
184 report_progress( 181 self.report_progress(
185 self,
186 "roots scanned", 182 "roots scanned",
187 state, 183 state,
188 Some(format!("{}/{}", n_done, n_total)), 184 Some(format!("{}/{}", n_done, n_total)),
189 Some(percentage(n_done, n_total)), 185 Some(Progress::percentage(n_done, n_total)),
190 ) 186 )
191 } 187 }
192 }, 188 },
193 Event::Flycheck(task) => match task { 189 Event::Flycheck(task) => match task {
194 flycheck::Message::ClearDiagnostics => self.diagnostics.clear_check(),
195
196 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { 190 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
197 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 191 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
198 &self.config.diagnostics, 192 &self.config.diagnostics,
@@ -215,14 +209,19 @@ impl GlobalState {
215 209
216 flycheck::Message::Progress(status) => { 210 flycheck::Message::Progress(status) => {
217 let (state, message) = match status { 211 let (state, message) = match status {
218 flycheck::Progress::Being => (Progress::Begin, None), 212 flycheck::Progress::DidStart => {
213 self.diagnostics.clear_check();
214 (Progress::Begin, None)
215 }
219 flycheck::Progress::DidCheckCrate(target) => { 216 flycheck::Progress::DidCheckCrate(target) => {
220 (Progress::Report, Some(target)) 217 (Progress::Report, Some(target))
221 } 218 }
222 flycheck::Progress::End => (Progress::End, None), 219 flycheck::Progress::DidFinish | flycheck::Progress::DidCancel => {
220 (Progress::End, None)
221 }
223 }; 222 };
224 223
225 report_progress(self, "cargo check", state, message, None); 224 self.report_progress("cargo check", state, message, None);
226 } 225 }
227 }, 226 },
228 } 227 }
@@ -248,10 +247,9 @@ impl GlobalState {
248 for file_id in diagnostic_changes { 247 for file_id in diagnostic_changes {
249 let url = file_id_to_url(&self.vfs.read().0, file_id); 248 let url = file_id_to_url(&self.vfs.read().0, file_id);
250 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); 249 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect();
251 let params = 250 self.send_notification::<lsp_types::notification::PublishDiagnostics>(
252 lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }; 251 lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None },
253 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); 252 );
254 self.send(not.into());
255 } 253 }
256 } 254 }
257 255
@@ -269,7 +267,7 @@ impl GlobalState {
269 } 267 }
270 268
271 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { 269 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
272 self.req_queue.incoming.register(req.id.clone(), (req.method.clone(), request_received)); 270 self.register_request(&req, request_received);
273 271
274 RequestDispatcher { req: Some(req), global_state: self } 272 RequestDispatcher { req: Some(req), global_state: self }
275 .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))? 273 .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))?
@@ -333,9 +331,7 @@ impl GlobalState {
333 lsp_types::NumberOrString::Number(id) => id.into(), 331 lsp_types::NumberOrString::Number(id) => id.into(),
334 lsp_types::NumberOrString::String(id) => id.into(), 332 lsp_types::NumberOrString::String(id) => id.into(),
335 }; 333 };
336 if let Some(response) = this.req_queue.incoming.cancel(id) { 334 this.cancel(id);
337 this.send(response.into());
338 }
339 Ok(()) 335 Ok(())
340 })? 336 })?
341 .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| { 337 .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
@@ -370,13 +366,13 @@ impl GlobalState {
370 this.loader.handle.invalidate(path.to_path_buf()); 366 this.loader.handle.invalidate(path.to_path_buf());
371 } 367 }
372 } 368 }
373 let params = lsp_types::PublishDiagnosticsParams { 369 this.send_notification::<lsp_types::notification::PublishDiagnostics>(
374 uri: params.text_document.uri, 370 lsp_types::PublishDiagnosticsParams {
375 diagnostics: Vec::new(), 371 uri: params.text_document.uri,
376 version: None, 372 diagnostics: Vec::new(),
377 }; 373 version: None,
378 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); 374 },
379 this.send(not.into()); 375 );
380 Ok(()) 376 Ok(())
381 })? 377 })?
382 .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| { 378 .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| {
@@ -388,8 +384,7 @@ impl GlobalState {
388 .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { 384 .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
389 // As stated in https://github.com/microsoft/language-server-protocol/issues/676, 385 // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
390 // this notification's parameters should be ignored and the actual config queried separately. 386 // this notification's parameters should be ignored and the actual config queried separately.
391 let request = this.req_queue.outgoing.register( 387 this.send_request::<lsp_types::request::WorkspaceConfiguration>(
392 lsp_types::request::WorkspaceConfiguration::METHOD.to_string(),
393 lsp_types::ConfigurationParams { 388 lsp_types::ConfigurationParams {
394 items: vec![lsp_types::ConfigurationItem { 389 items: vec![lsp_types::ConfigurationItem {
395 scope_uri: None, 390 scope_uri: None,
@@ -417,7 +412,6 @@ impl GlobalState {
417 } 412 }
418 }, 413 },
419 ); 414 );
420 this.send(request.into());
421 415
422 return Ok(()); 416 return Ok(());
423 })? 417 })?
@@ -465,60 +459,3 @@ impl GlobalState {
465 }); 459 });
466 } 460 }
467} 461}
468
469#[derive(Eq, PartialEq)]
470enum Progress {
471 Begin,
472 Report,
473 End,
474}
475
476fn percentage(done: usize, total: usize) -> f64 {
477 (done as f64 / total.max(1) as f64) * 100.0
478}
479
480fn report_progress(
481 global_state: &mut GlobalState,
482 title: &str,
483 state: Progress,
484 message: Option<String>,
485 percentage: Option<f64>,
486) {
487 if !global_state.config.client_caps.work_done_progress {
488 return;
489 }
490 let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
491 let work_done_progress = match state {
492 Progress::Begin => {
493 let work_done_progress_create = global_state.req_queue.outgoing.register(
494 lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(),
495 lsp_types::WorkDoneProgressCreateParams { token: token.clone() },
496 |_, _| (),
497 );
498 global_state.send(work_done_progress_create.into());
499
500 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
501 title: title.into(),
502 cancellable: None,
503 message,
504 percentage,
505 })
506 }
507 Progress::Report => {
508 lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
509 cancellable: None,
510 message,
511 percentage,
512 })
513 }
514 Progress::End => {
515 lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message })
516 }
517 };
518 let notification =
519 notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams {
520 token,
521 value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress),
522 });
523 global_state.send(notification.into());
524}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index a22d3e262..ec71f8b29 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -1,9 +1,8 @@
1//! Project loading & configuration updates 1//! Project loading & configuration updates
2use std::sync::Arc; 2use std::{mem, sync::Arc};
3 3
4use crossbeam_channel::unbounded; 4use crossbeam_channel::unbounded;
5use flycheck::FlycheckHandle; 5use flycheck::FlycheckHandle;
6use lsp_types::request::Request;
7use ra_db::{CrateGraph, SourceRoot, VfsPath}; 6use ra_db::{CrateGraph, SourceRoot, VfsPath};
8use ra_ide::AnalysisChange; 7use ra_ide::AnalysisChange;
9use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; 8use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace};
@@ -15,12 +14,14 @@ use crate::{
15}; 14};
16 15
17impl GlobalState { 16impl GlobalState {
18 pub(crate) fn update_configuration(&mut self, new_config: Config) { 17 pub(crate) fn update_configuration(&mut self, config: Config) {
19 self.analysis_host.update_lru_capacity(new_config.lru_capacity); 18 let old_config = mem::replace(&mut self.config, config);
20 if new_config.flycheck != self.config.flycheck { 19 if self.config.lru_capacity != old_config.lru_capacity {
20 self.analysis_host.update_lru_capacity(old_config.lru_capacity);
21 }
22 if self.config.flycheck != old_config.flycheck {
21 self.reload_flycheck(); 23 self.reload_flycheck();
22 } 24 }
23 self.config = new_config;
24 } 25 }
25 pub(crate) fn reload(&mut self) { 26 pub(crate) fn reload(&mut self) {
26 let workspaces = { 27 let workspaces = {
@@ -36,27 +37,31 @@ impl GlobalState {
36 self.config 37 self.config
37 .linked_projects 38 .linked_projects
38 .iter() 39 .iter()
39 .filter_map(|project| match project { 40 .map(|project| match project {
40 LinkedProject::ProjectManifest(manifest) => { 41 LinkedProject::ProjectManifest(manifest) => {
41 ra_project_model::ProjectWorkspace::load( 42 ra_project_model::ProjectWorkspace::load(
42 manifest.clone(), 43 manifest.clone(),
43 &self.config.cargo, 44 &self.config.cargo,
44 self.config.with_sysroot, 45 self.config.with_sysroot,
45 ) 46 )
46 .map_err(|err| {
47 log::error!("failed to load workspace: {:#}", err);
48 self.show_message(
49 lsp_types::MessageType::Error,
50 format!("rust-analyzer failed to load workspace: {:#}", err),
51 );
52 })
53 .ok()
54 } 47 }
55 LinkedProject::InlineJsonProject(it) => { 48 LinkedProject::InlineJsonProject(it) => {
56 Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) 49 Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() })
57 } 50 }
58 }) 51 })
59 .collect::<Vec<_>>() 52 .collect::<Vec<_>>()
53 .into_iter()
54 .filter_map(|res| {
55 res.map_err(|err| {
56 log::error!("failed to load workspace: {:#}", err);
57 self.show_message(
58 lsp_types::MessageType::Error,
59 format!("rust-analyzer failed to load workspace: {:#}", err),
60 );
61 })
62 .ok()
63 })
64 .collect::<Vec<_>>()
60 }; 65 };
61 66
62 if let FilesWatcher::Client = self.config.files.watcher { 67 if let FilesWatcher::Client = self.config.files.watcher {
@@ -74,13 +79,10 @@ impl GlobalState {
74 method: "workspace/didChangeWatchedFiles".to_string(), 79 method: "workspace/didChangeWatchedFiles".to_string(),
75 register_options: Some(serde_json::to_value(registration_options).unwrap()), 80 register_options: Some(serde_json::to_value(registration_options).unwrap()),
76 }; 81 };
77 let params = lsp_types::RegistrationParams { registrations: vec![registration] }; 82 self.send_request::<lsp_types::request::RegisterCapability>(
78 let request = self.req_queue.outgoing.register( 83 lsp_types::RegistrationParams { registrations: vec![registration] },
79 lsp_types::request::RegisterCapability::METHOD.to_string(),
80 params,
81 |_, _| (), 84 |_, _| (),
82 ); 85 );
83 self.send(request.into());
84 } 86 }
85 87
86 let mut change = AnalysisChange::new(); 88 let mut change = AnalysisChange::new();
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
index 0173f7464..d0ddeafe7 100644
--- a/crates/vfs/src/file_set.rs
+++ b/crates/vfs/src/file_set.rs
@@ -18,7 +18,7 @@ impl FileSet {
18 pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 18 pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
19 let mut base = self.paths[&anchor].clone(); 19 let mut base = self.paths[&anchor].clone();
20 base.pop(); 20 base.pop();
21 let path = base.join(path); 21 let path = base.join(path)?;
22 let res = self.files.get(&path).copied(); 22 let res = self.files.get(&path).copied();
23 res 23 res
24 } 24 }
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs
index 940f91d0e..dc3031ada 100644
--- a/crates/vfs/src/vfs_path.rs
+++ b/crates/vfs/src/vfs_path.rs
@@ -22,15 +22,15 @@ impl VfsPath {
22 VfsPathRepr::VirtualPath(_) => None, 22 VfsPathRepr::VirtualPath(_) => None,
23 } 23 }
24 } 24 }
25 pub fn join(&self, path: &str) -> VfsPath { 25 pub fn join(&self, path: &str) -> Option<VfsPath> {
26 match &self.0 { 26 match &self.0 {
27 VfsPathRepr::PathBuf(it) => { 27 VfsPathRepr::PathBuf(it) => {
28 let res = it.join(path).normalize(); 28 let res = it.join(path).normalize();
29 VfsPath(VfsPathRepr::PathBuf(res)) 29 Some(VfsPath(VfsPathRepr::PathBuf(res)))
30 } 30 }
31 VfsPathRepr::VirtualPath(it) => { 31 VfsPathRepr::VirtualPath(it) => {
32 let res = it.join(path); 32 let res = it.join(path)?;
33 VfsPath(VfsPathRepr::VirtualPath(res)) 33 Some(VfsPath(VfsPathRepr::VirtualPath(res)))
34 } 34 }
35 } 35 }
36 } 36 }
@@ -101,13 +101,15 @@ impl VirtualPath {
101 self.0 = self.0[..pos].to_string(); 101 self.0 = self.0[..pos].to_string();
102 true 102 true
103 } 103 }
104 fn join(&self, mut path: &str) -> VirtualPath { 104 fn join(&self, mut path: &str) -> Option<VirtualPath> {
105 let mut res = self.clone(); 105 let mut res = self.clone();
106 while path.starts_with("../") { 106 while path.starts_with("../") {
107 assert!(res.pop()); 107 if !res.pop() {
108 return None;
109 }
108 path = &path["../".len()..] 110 path = &path["../".len()..]
109 } 111 }
110 res.0 = format!("{}/{}", res.0, path); 112 res.0 = format!("{}/{}", res.0, path);
111 res 113 Some(res)
112 } 114 }
113} 115}