aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/semantics.rs1
-rw-r--r--crates/ra_ide/src/completion/complete_attribute.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs6
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_qualified_path.rs15
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs4
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs7
-rw-r--r--crates/ra_prof/Cargo.toml5
-rw-r--r--crates/ra_prof/src/lib.rs9
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs6
-rw-r--r--crates/rust-analyzer/src/global_state.rs23
-rw-r--r--crates/rust-analyzer/src/main_loop.rs73
-rw-r--r--crates/rust-analyzer/src/reload.rs56
-rw-r--r--crates/stdx/src/lib.rs2
-rw-r--r--crates/vfs-notify/src/include.rs1
-rw-r--r--crates/vfs-notify/src/lib.rs12
-rw-r--r--crates/vfs/src/lib.rs2
18 files changed, 155 insertions, 73 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 0d877e44e..4a16ac566 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -568,6 +568,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
568 node.ancestors().last().unwrap() 568 node.ancestors().last().unwrap()
569} 569}
570 570
571#[derive(Debug)]
571pub struct SemanticsScope<'a> { 572pub struct SemanticsScope<'a> {
572 pub db: &'a dyn HirDatabase, 573 pub db: &'a dyn HirDatabase,
573 resolver: Resolver, 574 resolver: Resolver,
diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index 9db317509..047299de9 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -195,7 +195,7 @@ fn parse_derive_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>,
195 195
196fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> { 196fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
197 let mut result = FxHashSet::default(); 197 let mut result = FxHashSet::default();
198 ctx.scope().process_all_names(&mut |name, scope_def| { 198 ctx.scope.process_all_names(&mut |name, scope_def| {
199 if let hir::ScopeDef::MacroDef(mac) = scope_def { 199 if let hir::ScopeDef::MacroDef(mac) = scope_def {
200 if mac.is_derive_macro() { 200 if mac.is_derive_macro() {
201 result.insert(name.to_string()); 201 result.insert(name.to_string());
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 3c6c8c81a..532665285 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -29,7 +29,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
29fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 29fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
30 for receiver in receiver.autoderef(ctx.db) { 30 for receiver in receiver.autoderef(ctx.db) {
31 for (field, ty) in receiver.fields(ctx.db) { 31 for (field, ty) in receiver.fields(ctx.db) {
32 if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 32 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
33 // Skip private field. FIXME: If the definition location of the 33 // Skip private field. FIXME: If the definition location of the
34 // field is editable, we should show the completion 34 // field is editable, we should show the completion
35 continue; 35 continue;
@@ -46,10 +46,10 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
46fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 46fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
47 if let Some(krate) = ctx.krate { 47 if let Some(krate) = ctx.krate {
48 let mut seen_methods = FxHashSet::default(); 48 let mut seen_methods = FxHashSet::default();
49 let traits_in_scope = ctx.scope().traits_in_scope(); 49 let traits_in_scope = ctx.scope.traits_in_scope();
50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { 50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
51 if func.has_self_param(ctx.db) 51 if func.has_self_param(ctx.db)
52 && ctx.scope().module().map_or(true, |m| func.is_visible_from(ctx.db, m)) 52 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
53 && seen_methods.insert(func.name(ctx.db)) 53 && seen_methods.insert(func.name(ctx.db))
54 { 54 {
55 acc.add_function(ctx, func, None); 55 acc.add_function(ctx, func, None);
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
index d6613ed7b..0447f0511 100644
--- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
+++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
@@ -5,7 +5,7 @@ use crate::completion::{CompletionContext, Completions};
5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level. 6 // Show only macros in top level.
7 if ctx.is_new_item { 7 if ctx.is_new_item {
8 ctx.scope().process_all_names(&mut |name, res| { 8 ctx.scope.process_all_names(&mut |name, res| {
9 if let hir::ScopeDef::MacroDef(mac) = res { 9 if let hir::ScopeDef::MacroDef(mac) = res {
10 acc.add_macro(ctx, Some(name.to_string()), mac); 10 acc.add_macro(ctx, Some(name.to_string()), mac);
11 } 11 }
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index 13fa7548d..aceb77cb5 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -13,7 +13,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
13 13
14 // FIXME: ideally, we should look at the type we are matching against and 14 // FIXME: ideally, we should look at the type we are matching against and
15 // suggest variants + auto-imports 15 // suggest variants + auto-imports
16 ctx.scope().process_all_names(&mut |name, res| { 16 ctx.scope.process_all_names(&mut |name, res| {
17 match &res { 17 match &res {
18 hir::ScopeDef::ModuleDef(def) => match def { 18 hir::ScopeDef::ModuleDef(def) => match def {
19 hir::ModuleDef::Adt(hir::Adt::Enum(..)) 19 hir::ModuleDef::Adt(hir::Adt::Enum(..))
diff --git a/crates/ra_ide/src/completion/complete_qualified_path.rs b/crates/ra_ide/src/completion/complete_qualified_path.rs
index e5553e28f..b08f5b9b4 100644
--- a/crates/ra_ide/src/completion/complete_qualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_qualified_path.rs
@@ -17,21 +17,20 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
17 return; 17 return;
18 } 18 }
19 19
20 let scope = ctx.scope(); 20 let context_module = ctx.scope.module();
21 let context_module = scope.module();
22 21
23 let res = match scope.resolve_hir_path_qualifier(&path) { 22 let resolution = match ctx.scope.resolve_hir_path_qualifier(&path) {
24 Some(res) => res, 23 Some(res) => res,
25 None => return, 24 None => return,
26 }; 25 };
27 26
28 // Add associated types on type parameters and `Self`. 27 // Add associated types on type parameters and `Self`.
29 res.assoc_type_shorthand_candidates(ctx.db, |alias| { 28 resolution.assoc_type_shorthand_candidates(ctx.db, |alias| {
30 acc.add_type_alias(ctx, alias); 29 acc.add_type_alias(ctx, alias);
31 None::<()> 30 None::<()>
32 }); 31 });
33 32
34 match res { 33 match resolution {
35 PathResolution::Def(hir::ModuleDef::Module(module)) => { 34 PathResolution::Def(hir::ModuleDef::Module(module)) => {
36 let module_scope = module.scope(ctx.db, context_module); 35 let module_scope = module.scope(ctx.db, context_module);
37 for (name, def) in module_scope { 36 for (name, def) in module_scope {
@@ -68,7 +67,7 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
68 67
69 let krate = ctx.krate; 68 let krate = ctx.krate;
70 if let Some(krate) = krate { 69 if let Some(krate) = krate {
71 let traits_in_scope = ctx.scope().traits_in_scope(); 70 let traits_in_scope = ctx.scope.traits_in_scope();
72 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { 71 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
73 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 72 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
74 return None; 73 return None;
@@ -113,13 +112,13 @@ pub(super) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
113 } 112 }
114 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => { 113 PathResolution::TypeParam(_) | PathResolution::SelfType(_) => {
115 if let Some(krate) = ctx.krate { 114 if let Some(krate) = ctx.krate {
116 let ty = match res { 115 let ty = match resolution {
117 PathResolution::TypeParam(param) => param.ty(ctx.db), 116 PathResolution::TypeParam(param) => param.ty(ctx.db),
118 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db), 117 PathResolution::SelfType(impl_def) => impl_def.target_ty(ctx.db),
119 _ => return, 118 _ => return,
120 }; 119 };
121 120
122 let traits_in_scope = ctx.scope().traits_in_scope(); 121 let traits_in_scope = ctx.scope.traits_in_scope();
123 let mut seen = FxHashSet::default(); 122 let mut seen = FxHashSet::default();
124 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { 123 ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
125 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) { 124 if context_module.map_or(false, |m| !item.is_visible_from(ctx.db, m)) {
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 18f4488b7..bd9551f35 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -25,7 +25,7 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
25 return; 25 return;
26 } 26 }
27 27
28 ctx.scope().process_all_names(&mut |name, res| { 28 ctx.scope.process_all_names(&mut |name, res| {
29 if ctx.use_item_syntax.is_some() { 29 if ctx.use_item_syntax.is_some() {
30 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { 30 if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) {
31 if name_ref.syntax().text() == name.to_string().as_str() { 31 if name_ref.syntax().text() == name.to_string().as_str() {
@@ -42,7 +42,7 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
42 if let Some(Adt::Enum(enum_data)) = ty.as_adt() { 42 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
43 let variants = enum_data.variants(ctx.db); 43 let variants = enum_data.variants(ctx.db);
44 44
45 let module = if let Some(module) = ctx.scope().module() { 45 let module = if let Some(module) = ctx.scope.module() {
46 // Compute path from the completion site if available. 46 // Compute path from the completion site if available.
47 module 47 module
48 } else { 48 } else {
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 02811a91e..3d93f7067 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -24,6 +24,7 @@ use test_utils::mark;
24#[derive(Debug)] 24#[derive(Debug)]
25pub(crate) struct CompletionContext<'a> { 25pub(crate) struct CompletionContext<'a> {
26 pub(super) sema: Semantics<'a, RootDatabase>, 26 pub(super) sema: Semantics<'a, RootDatabase>,
27 pub(super) scope: SemanticsScope<'a>,
27 pub(super) db: &'a RootDatabase, 28 pub(super) db: &'a RootDatabase,
28 pub(super) config: &'a CompletionConfig, 29 pub(super) config: &'a CompletionConfig,
29 pub(super) offset: TextSize, 30 pub(super) offset: TextSize,
@@ -106,8 +107,10 @@ impl<'a> CompletionContext<'a> {
106 let original_token = 107 let original_token =
107 original_file.syntax().token_at_offset(position.offset).left_biased()?; 108 original_file.syntax().token_at_offset(position.offset).left_biased()?;
108 let token = sema.descend_into_macros(original_token.clone()); 109 let token = sema.descend_into_macros(original_token.clone());
110 let scope = sema.scope_at_offset(&token.parent(), position.offset);
109 let mut ctx = CompletionContext { 111 let mut ctx = CompletionContext {
110 sema, 112 sema,
113 scope,
111 db, 114 db,
112 config, 115 config,
113 original_token, 116 original_token,
@@ -207,10 +210,6 @@ impl<'a> CompletionContext<'a> {
207 } 210 }
208 } 211 }
209 212
210 pub(crate) fn scope(&self) -> SemanticsScope<'_> {
211 self.sema.scope_at_offset(&self.token.parent(), self.offset)
212 }
213
214 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 213 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
215 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 214 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
216 let syntax_element = NodeOrToken::Token(fake_ident_token.clone()); 215 let syntax_element = NodeOrToken::Token(fake_ident_token.clone());
diff --git a/crates/ra_prof/Cargo.toml b/crates/ra_prof/Cargo.toml
index c33b5121a..eabfcebb0 100644
--- a/crates/ra_prof/Cargo.toml
+++ b/crates/ra_prof/Cargo.toml
@@ -20,3 +20,8 @@ jemalloc-ctl = { version = "0.3.3", optional = true }
20[features] 20[features]
21jemalloc = [ "jemallocator", "jemalloc-ctl" ] 21jemalloc = [ "jemallocator", "jemalloc-ctl" ]
22cpu_profiler = [] 22cpu_profiler = []
23
24# Uncomment to enable for the whole crate graph
25# default = [ "backtrace" ]
26# default = [ "jemalloc" ]
27# default = [ "cpu_profiler" ]
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 89df7f04b..7163a8424 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -43,6 +43,7 @@ pub struct Scope {
43} 43}
44 44
45impl Scope { 45impl Scope {
46 #[must_use]
46 pub fn enter() -> Scope { 47 pub fn enter() -> Scope {
47 let prev = IN_SCOPE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), true)); 48 let prev = IN_SCOPE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), true));
48 Scope { prev } 49 Scope { prev }
@@ -65,7 +66,8 @@ impl Drop for Scope {
65/// 2. Build with `cpu_profiler` feature. 66/// 2. Build with `cpu_profiler` feature.
66/// 3. Tun the code, the *raw* output would be in the `./out.profile` file. 67/// 3. Tun the code, the *raw* output would be in the `./out.profile` file.
67/// 4. Install pprof for visualization (https://github.com/google/pprof). 68/// 4. Install pprof for visualization (https://github.com/google/pprof).
68/// 5. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results. 69/// 5. Bump sampling frequency to once per ms: `export CPUPROFILE_FREQUENCY=1000`
70/// 6. Use something like `pprof -svg target/release/rust-analyzer ./out.profile` to see the results.
69/// 71///
70/// For example, here's how I run profiling on NixOS: 72/// For example, here's how I run profiling on NixOS:
71/// 73///
@@ -73,11 +75,16 @@ impl Drop for Scope {
73/// $ nix-shell -p gperftools --run \ 75/// $ nix-shell -p gperftools --run \
74/// 'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null' 76/// 'cargo run --release -p rust-analyzer -- parse < ~/projects/rustbench/parser.rs > /dev/null'
75/// ``` 77/// ```
78///
79/// See this diff for how to profile completions:
80///
81/// https://github.com/rust-analyzer/rust-analyzer/pull/5306
76#[derive(Debug)] 82#[derive(Debug)]
77pub struct CpuProfiler { 83pub struct CpuProfiler {
78 _private: (), 84 _private: (),
79} 85}
80 86
87#[must_use]
81pub fn cpu_profiler() -> CpuProfiler { 88pub fn cpu_profiler() -> CpuProfiler {
82 #[cfg(feature = "cpu_profiler")] 89 #[cfg(feature = "cpu_profiler")]
83 { 90 {
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 6d1154056..4182ca156 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -155,7 +155,7 @@ impl CargoWorkspace {
155 if let Some(target) = cargo_features.target.as_ref() { 155 if let Some(target) = cargo_features.target.as_ref() {
156 meta.other_options(vec![String::from("--filter-platform"), target.clone()]); 156 meta.other_options(vec![String::from("--filter-platform"), target.clone()]);
157 } 157 }
158 let meta = meta.exec().with_context(|| { 158 let mut meta = meta.exec().with_context(|| {
159 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) 159 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
160 })?; 160 })?;
161 161
@@ -175,6 +175,7 @@ impl CargoWorkspace {
175 175
176 let ws_members = &meta.workspace_members; 176 let ws_members = &meta.workspace_members;
177 177
178 meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
178 for meta_pkg in meta.packages { 179 for meta_pkg in meta.packages {
179 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = 180 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
180 meta_pkg; 181 meta_pkg;
@@ -210,7 +211,7 @@ impl CargoWorkspace {
210 } 211 }
211 } 212 }
212 let resolve = meta.resolve.expect("metadata executed with deps"); 213 let resolve = meta.resolve.expect("metadata executed with deps");
213 for node in resolve.nodes { 214 for mut node in resolve.nodes {
214 let source = match pkg_by_id.get(&node.id) { 215 let source = match pkg_by_id.get(&node.id) {
215 Some(&src) => src, 216 Some(&src) => src,
216 // FIXME: replace this and a similar branch below with `.unwrap`, once 217 // FIXME: replace this and a similar branch below with `.unwrap`, once
@@ -221,6 +222,7 @@ impl CargoWorkspace {
221 continue; 222 continue;
222 } 223 }
223 }; 224 };
225 node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
224 for dep_node in node.deps { 226 for dep_node in node.deps {
225 let pkg = match pkg_by_id.get(&dep_node.pkg) { 227 let pkg = match pkg_by_id.get(&dep_node.pkg) {
226 Some(&pkg) => pkg, 228 Some(&pkg) => pkg,
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index c8d34e15a..9a9a6547a 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -26,6 +26,7 @@ use crate::{
26 to_proto::url_from_abs_path, 26 to_proto::url_from_abs_path,
27 Result, 27 Result,
28}; 28};
29use ra_prof::profile;
29 30
30#[derive(Eq, PartialEq, Copy, Clone)] 31#[derive(Eq, PartialEq, Copy, Clone)]
31pub(crate) enum Status { 32pub(crate) enum Status {
@@ -122,6 +123,10 @@ impl GlobalState {
122 } 123 }
123 124
124 pub(crate) fn process_changes(&mut self) -> bool { 125 pub(crate) fn process_changes(&mut self) -> bool {
126 let _p = profile("GlobalState::process_changes");
127 let mut fs_changes = Vec::new();
128 let mut has_fs_changes = false;
129
125 let change = { 130 let change = {
126 let mut change = AnalysisChange::new(); 131 let mut change = AnalysisChange::new();
127 let (vfs, line_endings_map) = &mut *self.vfs.write(); 132 let (vfs, line_endings_map) = &mut *self.vfs.write();
@@ -130,13 +135,14 @@ impl GlobalState {
130 return false; 135 return false;
131 } 136 }
132 137
133 let fs_op = changed_files.iter().any(|it| it.is_created_or_deleted());
134 if fs_op {
135 let roots = self.source_root_config.partition(&vfs);
136 change.set_roots(roots)
137 }
138
139 for file in changed_files { 138 for file in changed_files {
139 if file.is_created_or_deleted() {
140 if let Some(path) = vfs.file_path(file.file_id).as_path() {
141 fs_changes.push((path.to_path_buf(), file.change_kind));
142 has_fs_changes = true;
143 }
144 }
145
140 let text = if file.exists() { 146 let text = if file.exists() {
141 let bytes = vfs.file_contents(file.file_id).to_vec(); 147 let bytes = vfs.file_contents(file.file_id).to_vec();
142 match String::from_utf8(bytes).ok() { 148 match String::from_utf8(bytes).ok() {
@@ -152,10 +158,15 @@ impl GlobalState {
152 }; 158 };
153 change.change_file(file.file_id, text); 159 change.change_file(file.file_id, text);
154 } 160 }
161 if has_fs_changes {
162 let roots = self.source_root_config.partition(&vfs);
163 change.set_roots(roots);
164 }
155 change 165 change
156 }; 166 };
157 167
158 self.analysis_host.apply_change(change); 168 self.analysis_host.apply_change(change);
169 self.maybe_refresh(&fs_changes);
159 true 170 true
160 } 171 }
161 172
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 96e2399ce..702f25a19 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -22,6 +22,7 @@ use crate::{
22 Result, 22 Result,
23}; 23};
24use ra_project_model::ProjectWorkspace; 24use ra_project_model::ProjectWorkspace;
25use vfs::ChangeKind;
25 26
26pub fn main_loop(config: Config, connection: Connection) -> Result<()> { 27pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
27 log::info!("initial config: {:#?}", config); 28 log::info!("initial config: {:#?}", config);
@@ -197,39 +198,49 @@ impl GlobalState {
197 } 198 }
198 self.analysis_host.maybe_collect_garbage(); 199 self.analysis_host.maybe_collect_garbage();
199 } 200 }
200 Event::Vfs(task) => match task { 201 Event::Vfs(mut task) => {
201 vfs::loader::Message::Loaded { files } => { 202 let _p = profile("GlobalState::handle_event/vfs");
202 let vfs = &mut self.vfs.write().0; 203 loop {
203 for (path, contents) in files { 204 match task {
204 let path = VfsPath::from(path); 205 vfs::loader::Message::Loaded { files } => {
205 if !self.mem_docs.contains(&path) { 206 let vfs = &mut self.vfs.write().0;
206 vfs.set_file_contents(path, contents) 207 for (path, contents) in files {
208 let path = VfsPath::from(path);
209 if !self.mem_docs.contains(&path) {
210 vfs.set_file_contents(path, contents)
211 }
212 }
213 }
214 vfs::loader::Message::Progress { n_total, n_done } => {
215 if n_total == 0 {
216 self.transition(Status::Invalid);
217 } else {
218 let state = if n_done == 0 {
219 self.transition(Status::Loading);
220 Progress::Begin
221 } else if n_done < n_total {
222 Progress::Report
223 } else {
224 assert_eq!(n_done, n_total);
225 self.transition(Status::Ready);
226 Progress::End
227 };
228 self.report_progress(
229 "roots scanned",
230 state,
231 Some(format!("{}/{}", n_done, n_total)),
232 Some(Progress::percentage(n_done, n_total)),
233 )
234 }
207 } 235 }
208 } 236 }
209 } 237 // Coalesce many VFS event into a single loop turn
210 vfs::loader::Message::Progress { n_total, n_done } => { 238 task = match self.loader.receiver.try_recv() {
211 if n_total == 0 { 239 Ok(task) => task,
212 self.transition(Status::Invalid); 240 Err(_) => break,
213 } else {
214 let state = if n_done == 0 {
215 self.transition(Status::Loading);
216 Progress::Begin
217 } else if n_done < n_total {
218 Progress::Report
219 } else {
220 assert_eq!(n_done, n_total);
221 self.transition(Status::Ready);
222 Progress::End
223 };
224 self.report_progress(
225 "roots scanned",
226 state,
227 Some(format!("{}/{}", n_done, n_total)),
228 Some(Progress::percentage(n_done, n_total)),
229 )
230 } 241 }
231 } 242 }
232 }, 243 }
233 Event::Flycheck(task) => match task { 244 Event::Flycheck(task) => match task {
234 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { 245 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
235 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 246 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
@@ -428,7 +439,9 @@ impl GlobalState {
428 if let Some(flycheck) = &this.flycheck { 439 if let Some(flycheck) = &this.flycheck {
429 flycheck.handle.update(); 440 flycheck.handle.update();
430 } 441 }
431 this.maybe_refresh(params.text_document.uri.as_str()); 442 if let Ok(abs_path) = from_proto::abs_path(&params.text_document.uri) {
443 this.maybe_refresh(&[(abs_path, ChangeKind::Modify)]);
444 }
432 Ok(()) 445 Ok(())
433 })? 446 })?
434 .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { 447 .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 0a201fceb..ab1b18ea9 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -6,7 +6,7 @@ use flycheck::FlycheckHandle;
6use ra_db::{CrateGraph, SourceRoot, VfsPath}; 6use ra_db::{CrateGraph, SourceRoot, VfsPath};
7use ra_ide::AnalysisChange; 7use ra_ide::AnalysisChange;
8use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; 8use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace};
9use vfs::{file_set::FileSetConfig, AbsPath}; 9use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
10 10
11use crate::{ 11use crate::{
12 config::{Config, FilesWatcher, LinkedProject}, 12 config::{Config, FilesWatcher, LinkedProject},
@@ -14,9 +14,11 @@ use crate::{
14 lsp_ext, 14 lsp_ext,
15 main_loop::Task, 15 main_loop::Task,
16}; 16};
17use ra_prof::profile;
17 18
18impl GlobalState { 19impl GlobalState {
19 pub(crate) fn update_configuration(&mut self, config: Config) { 20 pub(crate) fn update_configuration(&mut self, config: Config) {
21 let _p = profile("GlobalState::update_configuration");
20 let old_config = mem::replace(&mut self.config, config); 22 let old_config = mem::replace(&mut self.config, config);
21 if self.config.lru_capacity != old_config.lru_capacity { 23 if self.config.lru_capacity != old_config.lru_capacity {
22 self.analysis_host.update_lru_capacity(old_config.lru_capacity); 24 self.analysis_host.update_lru_capacity(old_config.lru_capacity);
@@ -27,8 +29,8 @@ impl GlobalState {
27 self.reload_flycheck(); 29 self.reload_flycheck();
28 } 30 }
29 } 31 }
30 pub(crate) fn maybe_refresh(&mut self, saved_doc_url: &str) { 32 pub(crate) fn maybe_refresh(&mut self, changes: &[(AbsPathBuf, ChangeKind)]) {
31 if !(saved_doc_url.ends_with("Cargo.toml") || saved_doc_url.ends_with("Cargo.lock")) { 33 if !changes.iter().any(|(path, kind)| is_interesting(path, *kind)) {
32 return; 34 return;
33 } 35 }
34 match self.status { 36 match self.status {
@@ -40,6 +42,41 @@ impl GlobalState {
40 } else { 42 } else {
41 self.transition(Status::NeedsReload); 43 self.transition(Status::NeedsReload);
42 } 44 }
45
46 fn is_interesting(path: &AbsPath, change_kind: ChangeKind) -> bool {
47 const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
48 const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
49
50 if path.ends_with("Cargo.toml") || path.ends_with("Cargo.lock") {
51 return true;
52 }
53 if change_kind == ChangeKind::Modify {
54 return false;
55 }
56 if path.extension().unwrap_or_default() != "rs" {
57 return false;
58 }
59 if IMPLICIT_TARGET_FILES.iter().any(|it| path.ends_with(it)) {
60 return true;
61 }
62 let parent = match path.parent() {
63 Some(it) => it,
64 None => return false,
65 };
66 if IMPLICIT_TARGET_DIRS.iter().any(|it| parent.ends_with(it)) {
67 return true;
68 }
69 if path.ends_with("main.rs") {
70 let grand_parent = match parent.parent() {
71 Some(it) => it,
72 None => return false,
73 };
74 if IMPLICIT_TARGET_DIRS.iter().any(|it| grand_parent.ends_with(it)) {
75 return true;
76 }
77 }
78 false
79 }
43 } 80 }
44 pub(crate) fn transition(&mut self, new_status: Status) { 81 pub(crate) fn transition(&mut self, new_status: Status) {
45 self.status = new_status; 82 self.status = new_status;
@@ -79,6 +116,7 @@ impl GlobalState {
79 }); 116 });
80 } 117 }
81 pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) { 118 pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) {
119 let _p = profile("GlobalState::switch_workspaces");
82 log::info!("reloading projects: {:?}", self.config.linked_projects); 120 log::info!("reloading projects: {:?}", self.config.linked_projects);
83 121
84 let mut has_errors = false; 122 let mut has_errors = false;
@@ -88,10 +126,12 @@ impl GlobalState {
88 res.map_err(|err| { 126 res.map_err(|err| {
89 has_errors = true; 127 has_errors = true;
90 log::error!("failed to load workspace: {:#}", err); 128 log::error!("failed to load workspace: {:#}", err);
91 self.show_message( 129 if self.workspaces.is_empty() {
92 lsp_types::MessageType::Error, 130 self.show_message(
93 format!("rust-analyzer failed to load workspace: {:#}", err), 131 lsp_types::MessageType::Error,
94 ); 132 format!("rust-analyzer failed to load workspace: {:#}", err),
133 );
134 }
95 }) 135 })
96 .ok() 136 .ok()
97 }) 137 })
@@ -144,6 +184,7 @@ impl GlobalState {
144 } 184 }
145 }, 185 },
146 }; 186 };
187
147 let watch = match self.config.files.watcher { 188 let watch = match self.config.files.watcher {
148 FilesWatcher::Client => vec![], 189 FilesWatcher::Client => vec![],
149 FilesWatcher::Notify => project_folders.watch, 190 FilesWatcher::Notify => project_folders.watch,
@@ -267,6 +308,7 @@ pub(crate) struct SourceRootConfig {
267 308
268impl SourceRootConfig { 309impl SourceRootConfig {
269 pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> { 310 pub(crate) fn partition(&self, vfs: &vfs::Vfs) -> Vec<SourceRoot> {
311 let _p = profile("SourceRootConfig::partition");
270 self.fsc 312 self.fsc
271 .partition(vfs) 313 .partition(vfs)
272 .into_iter() 314 .into_iter()
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 08ac6f70f..3b605c79d 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -87,6 +87,8 @@ where
87 Ok(()) 87 Ok(())
88 } 88 }
89} 89}
90
91#[must_use]
90pub fn timeit(label: &'static str) -> impl Drop { 92pub fn timeit(label: &'static str) -> impl Drop {
91 struct Guard { 93 struct Guard {
92 label: &'static str, 94 label: &'static str,
diff --git a/crates/vfs-notify/src/include.rs b/crates/vfs-notify/src/include.rs
index 72f928536..59da3d77a 100644
--- a/crates/vfs-notify/src/include.rs
+++ b/crates/vfs-notify/src/include.rs
@@ -1,5 +1,4 @@
1//! See `Include`. 1//! See `Include`.
2
3use std::convert::TryFrom; 2use std::convert::TryFrom;
4 3
5use globset::{Glob, GlobSet, GlobSetBuilder}; 4use globset::{Glob, GlobSet, GlobSetBuilder};
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs
index ee5035554..6aaa53f63 100644
--- a/crates/vfs-notify/src/lib.rs
+++ b/crates/vfs-notify/src/lib.rs
@@ -82,11 +82,13 @@ impl NotifyActor {
82 Event::Message(msg) => match msg { 82 Event::Message(msg) => match msg {
83 Message::Config(config) => { 83 Message::Config(config) => {
84 self.watcher = None; 84 self.watcher = None;
85 let (watcher_sender, watcher_receiver) = unbounded(); 85 if !config.watch.is_empty() {
86 let watcher = log_notify_error(Watcher::new_immediate(move |event| { 86 let (watcher_sender, watcher_receiver) = unbounded();
87 watcher_sender.send(event).unwrap() 87 let watcher = log_notify_error(Watcher::new_immediate(move |event| {
88 })); 88 watcher_sender.send(event).unwrap()
89 self.watcher = watcher.map(|it| (it, watcher_receiver)); 89 }));
90 self.watcher = watcher.map(|it| (it, watcher_receiver));
91 }
90 92
91 let n_total = config.load.len(); 93 let n_total = config.load.len();
92 self.send(loader::Message::Progress { n_total, n_done: 0 }); 94 self.send(loader::Message::Progress { n_total, n_done: 0 });
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index 024e58018..3bfecd08f 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -70,7 +70,7 @@ impl ChangedFile {
70 } 70 }
71} 71}
72 72
73#[derive(Eq, PartialEq)] 73#[derive(Eq, PartialEq, Copy, Clone, Debug)]
74pub enum ChangeKind { 74pub enum ChangeKind {
75 Create, 75 Create,
76 Modify, 76 Modify,