aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/code_model.rs8
-rw-r--r--crates/ra_hir_def/src/attr.rs8
-rw-r--r--crates/ra_hir_def/src/data.rs6
-rw-r--r--crates/ra_hir_def/src/docs.rs50
-rw-r--r--crates/ra_hir_expand/src/name.rs1
-rw-r--r--crates/ra_ide/src/completion.rs78
-rw-r--r--crates/ra_ide/src/display/function_signature.rs13
-rw-r--r--crates/ra_ide/src/hover.rs127
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html1
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html1
-rw-r--r--crates/ra_ide/src/snapshots/highlight_unsafe.html48
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html1
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html1
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs25
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs1
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs8
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs31
-rw-r--r--crates/ra_parser/src/grammar.rs7
-rw-r--r--crates/ra_project_model/src/json_project.rs89
-rw-r--r--crates/ra_project_model/src/lib.rs60
-rw-r--r--crates/ra_syntax/src/ast/traits.rs13
-rw-r--r--crates/rust-analyzer/src/bin/main.rs41
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs4
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs5
-rw-r--r--crates/rust-analyzer/src/config.rs48
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs3
-rw-r--r--crates/rust-analyzer/src/from_proto.rs8
-rw-r--r--crates/rust-analyzer/src/global_state.rs (renamed from crates/rust-analyzer/src/world.rs)42
-rw-r--r--crates/rust-analyzer/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs147
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs390
-rw-r--r--crates/rust-analyzer/src/to_proto.rs88
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs54
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs31
-rw-r--r--crates/stdx/src/lib.rs5
-rw-r--r--crates/test_utils/Cargo.toml3
-rw-r--r--crates/test_utils/src/lib.rs6
38 files changed, 979 insertions, 477 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index e40aeffbc..4a06f3bcd 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -637,6 +637,10 @@ impl Function {
637 db.function_data(self.id).params.clone() 637 db.function_data(self.id).params.clone()
638 } 638 }
639 639
640 pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
641 db.function_data(self.id).is_unsafe
642 }
643
640 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 644 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
641 let _p = profile("Function::diagnostics"); 645 let _p = profile("Function::diagnostics");
642 let infer = db.infer(self.id.into()); 646 let infer = db.infer(self.id.into());
@@ -1190,6 +1194,10 @@ impl Type {
1190 ) 1194 )
1191 } 1195 }
1192 1196
1197 pub fn is_raw_ptr(&self) -> bool {
1198 matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }))
1199 }
1200
1193 pub fn contains_unknown(&self) -> bool { 1201 pub fn contains_unknown(&self) -> bool {
1194 return go(&self.ty.value); 1202 return go(&self.ty.value);
1195 1203
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 8b6c0bede..2eeba0572 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -87,12 +87,18 @@ impl Attrs {
87 } 87 }
88 88
89 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { 89 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
90 let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map(
91 |docs_text| Attr {
92 input: Some(AttrInput::Literal(SmolStr::new(docs_text))),
93 path: ModPath::from(hir_expand::name!(doc)),
94 },
95 );
90 let mut attrs = owner.attrs().peekable(); 96 let mut attrs = owner.attrs().peekable();
91 let entries = if attrs.peek().is_none() { 97 let entries = if attrs.peek().is_none() {
92 // Avoid heap allocation 98 // Avoid heap allocation
93 None 99 None
94 } else { 100 } else {
95 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect()) 101 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect())
96 }; 102 };
97 Attrs { entries } 103 Attrs { entries }
98 } 104 }
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index e2130d931..807195d25 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -34,6 +34,7 @@ pub struct FunctionData {
34 /// True if the first param is `self`. This is relevant to decide whether this 34 /// True if the first param is `self`. This is relevant to decide whether this
35 /// can be called as a method. 35 /// can be called as a method.
36 pub has_self_param: bool, 36 pub has_self_param: bool,
37 pub is_unsafe: bool,
37 pub visibility: RawVisibility, 38 pub visibility: RawVisibility,
38} 39}
39 40
@@ -85,11 +86,14 @@ impl FunctionData {
85 ret_type 86 ret_type
86 }; 87 };
87 88
89 let is_unsafe = src.value.unsafe_token().is_some();
90
88 let vis_default = RawVisibility::default_for_container(loc.container); 91 let vis_default = RawVisibility::default_for_container(loc.container);
89 let visibility = 92 let visibility =
90 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); 93 RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility()));
91 94
92 let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs }; 95 let sig =
96 FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs };
93 Arc::new(sig) 97 Arc::new(sig)
94 } 98 }
95} 99}
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs
index b221ae1ce..2630b3d89 100644
--- a/crates/ra_hir_def/src/docs.rs
+++ b/crates/ra_hir_def/src/docs.rs
@@ -29,6 +29,13 @@ impl Documentation {
29 Documentation(s.into()) 29 Documentation(s.into())
30 } 30 }
31 31
32 pub fn from_ast<N>(node: &N) -> Option<Documentation>
33 where
34 N: ast::DocCommentsOwner + ast::AttrsOwner,
35 {
36 docs_from_ast(node)
37 }
38
32 pub fn as_str(&self) -> &str { 39 pub fn as_str(&self) -> &str {
33 &*self.0 40 &*self.0
34 } 41 }
@@ -70,6 +77,45 @@ impl Documentation {
70 } 77 }
71} 78}
72 79
73pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { 80pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
74 node.doc_comment_text().map(|it| Documentation::new(&it)) 81where
82 N: ast::DocCommentsOwner + ast::AttrsOwner,
83{
84 let doc_comment_text = node.doc_comment_text();
85 let doc_attr_text = expand_doc_attrs(node);
86 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
87 docs.map(|it| Documentation::new(&it))
88}
89
90fn merge_doc_comments_and_attrs(
91 doc_comment_text: Option<String>,
92 doc_attr_text: Option<String>,
93) -> Option<String> {
94 match (doc_comment_text, doc_attr_text) {
95 (Some(mut comment_text), Some(attr_text)) => {
96 comment_text.push_str("\n\n");
97 comment_text.push_str(&attr_text);
98 Some(comment_text)
99 }
100 (Some(comment_text), None) => Some(comment_text),
101 (None, Some(attr_text)) => Some(attr_text),
102 (None, None) => None,
103 }
104}
105
106fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
107 let mut docs = String::new();
108 for attr in owner.attrs() {
109 if let Some(("doc", value)) =
110 attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
111 {
112 docs.push_str(value);
113 docs.push_str("\n\n");
114 }
115 }
116 if docs.is_empty() {
117 None
118 } else {
119 Some(docs.trim_end_matches("\n\n").to_owned())
120 }
75} 121}
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index ea495cb11..660bdfe33 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -153,6 +153,7 @@ pub mod known {
153 str, 153 str,
154 // Special names 154 // Special names
155 macro_rules, 155 macro_rules,
156 doc,
156 // Components of known path (value or mod name) 157 // Components of known path (value or mod name)
157 std, 158 std,
158 core, 159 core,
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index d890b69d2..a721e23c6 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -125,3 +125,81 @@ pub(crate) fn completions(
125 125
126 Some(acc) 126 Some(acc)
127} 127}
128
129#[cfg(test)]
130mod tests {
131 use crate::completion::completion_config::CompletionConfig;
132 use crate::mock_analysis::analysis_and_position;
133
134 struct DetailAndDocumentation<'a> {
135 detail: &'a str,
136 documentation: &'a str,
137 }
138
139 fn check_detail_and_documentation(fixture: &str, expected: DetailAndDocumentation) {
140 let (analysis, position) = analysis_and_position(fixture);
141 let config = CompletionConfig::default();
142 let completions = analysis.completions(&config, position).unwrap().unwrap();
143 for item in completions {
144 if item.detail() == Some(expected.detail) {
145 let opt = item.documentation();
146 let doc = opt.as_ref().map(|it| it.as_str());
147 assert_eq!(doc, Some(expected.documentation));
148 return;
149 }
150 }
151 panic!("completion detail not found: {}", expected.detail)
152 }
153
154 #[test]
155 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
156 check_detail_and_documentation(
157 r#"
158 //- /lib.rs
159 macro_rules! bar {
160 () => {
161 struct Bar;
162 impl Bar {
163 #[doc = "Do the foo"]
164 fn foo(&self) {}
165 }
166 }
167 }
168
169 bar!();
170
171 fn foo() {
172 let bar = Bar;
173 bar.fo<|>;
174 }
175 "#,
176 DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
177 );
178 }
179
180 #[test]
181 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
182 check_detail_and_documentation(
183 r#"
184 //- /lib.rs
185 macro_rules! bar {
186 () => {
187 struct Bar;
188 impl Bar {
189 /// Do the foo
190 fn foo(&self) {}
191 }
192 }
193 }
194
195 bar!();
196
197 fn foo() {
198 let bar = Bar;
199 bar.fo<|>;
200 }
201 "#,
202 DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
203 );
204 }
205}
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 9572debd8..ca8a6a650 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -10,7 +10,7 @@ use std::{
10use hir::{Docs, Documentation, HasSource, HirDisplay}; 10use hir::{Docs, Documentation, HasSource, HirDisplay};
11use ra_ide_db::RootDatabase; 11use ra_ide_db::RootDatabase;
12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 12use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
13use stdx::SepBy; 13use stdx::{split1, SepBy};
14 14
15use crate::display::{generic_parameters, where_predicates}; 15use crate::display::{generic_parameters, where_predicates};
16 16
@@ -207,7 +207,16 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
207 res.push(raw_param); 207 res.push(raw_param);
208 } 208 }
209 209
210 res.extend(param_list.params().map(|param| param.syntax().text().to_string())); 210 // macro-generated functions are missing whitespace
211 fn fmt_param(param: ast::Param) -> String {
212 let text = param.syntax().text().to_string();
213 match split1(&text, ':') {
214 Some((left, right)) => format!("{}: {}", left.trim(), right.trim()),
215 _ => text,
216 }
217 }
218
219 res.extend(param_list.params().map(fmt_param));
211 res_types.extend(param_list.params().map(|param| { 220 res_types.extend(param_list.params().map(|param| {
212 let param_text = param.syntax().text().to_string(); 221 let param_text = param.syntax().text().to_string();
213 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { 222 match param_text.split(':').nth(1).and_then(|it| it.get(1..)) {
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index d96cb5596..9636cd0d6 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,8 +1,8 @@
1use std::iter::once; 1use std::iter::once;
2 2
3use hir::{ 3use hir::{
4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 4 Adt, AsAssocItem, AssocItemContainer, Documentation, FieldSource, HasSource, HirDisplay,
5 ModuleSource, Semantics, 5 ModuleDef, ModuleSource, Semantics,
6}; 6};
7use itertools::Itertools; 7use itertools::Itertools;
8use ra_db::SourceDatabase; 8use ra_db::SourceDatabase;
@@ -10,12 +10,7 @@ use ra_ide_db::{
10 defs::{classify_name, classify_name_ref, Definition}, 10 defs::{classify_name, classify_name_ref, Definition},
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use ra_syntax::{ 13use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
14 ast::{self, DocCommentsOwner},
15 match_ast, AstNode,
16 SyntaxKind::*,
17 SyntaxToken, TokenAtOffset,
18};
19 14
20use crate::{ 15use crate::{
21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 16 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
@@ -169,13 +164,15 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
169 return match def { 164 return match def {
170 Definition::Macro(it) => { 165 Definition::Macro(it) => {
171 let src = it.source(db); 166 let src = it.source(db);
172 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) 167 let docs = Documentation::from_ast(&src.value).map(Into::into);
168 hover_text(docs, Some(macro_label(&src.value)), mod_path)
173 } 169 }
174 Definition::Field(it) => { 170 Definition::Field(it) => {
175 let src = it.source(db); 171 let src = it.source(db);
176 match src.value { 172 match src.value {
177 FieldSource::Named(it) => { 173 FieldSource::Named(it) => {
178 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 174 let docs = Documentation::from_ast(&it).map(Into::into);
175 hover_text(docs, it.short_label(), mod_path)
179 } 176 }
180 _ => None, 177 _ => None,
181 } 178 }
@@ -183,7 +180,8 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
183 Definition::ModuleDef(it) => match it { 180 Definition::ModuleDef(it) => match it {
184 ModuleDef::Module(it) => match it.definition_source(db).value { 181 ModuleDef::Module(it) => match it.definition_source(db).value {
185 ModuleSource::Module(it) => { 182 ModuleSource::Module(it) => {
186 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 183 let docs = Documentation::from_ast(&it).map(Into::into);
184 hover_text(docs, it.short_label(), mod_path)
187 } 185 }
188 _ => None, 186 _ => None,
189 }, 187 },
@@ -208,10 +206,11 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
208 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> 206 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String>
209 where 207 where
210 D: HasSource<Ast = A>, 208 D: HasSource<Ast = A>,
211 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, 209 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
212 { 210 {
213 let src = def.source(db); 211 let src = def.source(db);
214 hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) 212 let docs = Documentation::from_ast(&src.value).map(Into::into);
213 hover_text(docs, src.value.short_label(), mod_path)
215 } 214 }
216} 215}
217 216
@@ -951,4 +950,106 @@ fn func(foo: i32) { if true { <|>foo; }; }
951 &["mod my"], 950 &["mod my"],
952 ); 951 );
953 } 952 }
953
954 #[test]
955 fn test_hover_struct_doc_comment() {
956 check_hover_result(
957 r#"
958 //- /lib.rs
959 /// bar docs
960 struct Bar;
961
962 fn foo() {
963 let bar = Ba<|>r;
964 }
965 "#,
966 &["struct Bar\n```\n___\n\nbar docs"],
967 );
968 }
969
970 #[test]
971 fn test_hover_struct_doc_attr() {
972 check_hover_result(
973 r#"
974 //- /lib.rs
975 #[doc = "bar docs"]
976 struct Bar;
977
978 fn foo() {
979 let bar = Ba<|>r;
980 }
981 "#,
982 &["struct Bar\n```\n___\n\nbar docs"],
983 );
984 }
985
986 #[test]
987 fn test_hover_struct_doc_attr_multiple_and_mixed() {
988 check_hover_result(
989 r#"
990 //- /lib.rs
991 /// bar docs 0
992 #[doc = "bar docs 1"]
993 #[doc = "bar docs 2"]
994 struct Bar;
995
996 fn foo() {
997 let bar = Ba<|>r;
998 }
999 "#,
1000 &["struct Bar\n```\n___\n\nbar docs 0\n\nbar docs 1\n\nbar docs 2"],
1001 );
1002 }
1003
1004 #[test]
1005 fn test_hover_macro_generated_struct_fn_doc_comment() {
1006 check_hover_result(
1007 r#"
1008 //- /lib.rs
1009 macro_rules! bar {
1010 () => {
1011 struct Bar;
1012 impl Bar {
1013 /// Do the foo
1014 fn foo(&self) {}
1015 }
1016 }
1017 }
1018
1019 bar!();
1020
1021 fn foo() {
1022 let bar = Bar;
1023 bar.fo<|>o();
1024 }
1025 "#,
1026 &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\n Do the foo"],
1027 );
1028 }
1029
1030 #[test]
1031 fn test_hover_macro_generated_struct_fn_doc_attr() {
1032 check_hover_result(
1033 r#"
1034 //- /lib.rs
1035 macro_rules! bar {
1036 () => {
1037 struct Bar;
1038 impl Bar {
1039 #[doc = "Do the foo"]
1040 fn foo(&self) {}
1041 }
1042 }
1043 }
1044
1045 bar!();
1046
1047 fn foo() {
1048 let bar = Bar;
1049 bar.fo<|>o();
1050 }
1051 "#,
1052 &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"],
1053 );
1054 }
954} 1055}
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index 68fc589bc..fcdc98201 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
13.parameter { color: #94BFF3; } 14.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 15.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 16.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 41cddd0ff..e97192b61 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
13.parameter { color: #94BFF3; } 14.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 15.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 16.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/src/snapshots/highlight_unsafe.html
new file mode 100644
index 000000000..17ffc727c
--- /dev/null
+++ b/crates/ra_ide/src/snapshots/highlight_unsafe.html
@@ -0,0 +1,48 @@
1
2<style>
3body { margin: 0; }
4pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
5
6.lifetime { color: #DFAF8F; font-style: italic; }
7.comment { color: #7F9F7F; }
8.struct, .enum { color: #7CB8BB; }
9.enum_variant { color: #BDE0F3; }
10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; }
12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
14.parameter { color: #94BFF3; }
15.text { color: #DCDCCC; }
16.type { color: #7CB8BB; }
17.builtin_type { color: #8CD0D3; }
18.type_param { color: #DFAF8F; }
19.attribute { color: #94BFF3; }
20.numeric_literal { color: #BFEBBF; }
21.bool_literal { color: #BFE6EB; }
22.macro { color: #94BFF3; }
23.module { color: #AFD8AF; }
24.variable { color: #DCDCCC; }
25.format_specifier { color: #CC696B; }
26.mutable { text-decoration: underline; }
27
28.keyword { color: #F0DFAF; font-weight: bold; }
29.keyword.unsafe { color: #BC8383; font-weight: bold; }
30.control { font-style: italic; }
31</style>
32<pre><code><span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_fn</span>() {}
33
34<span class="keyword">struct</span> <span class="struct declaration">HasUnsafeFn</span>;
35
36<span class="keyword">impl</span> <span class="struct">HasUnsafeFn</span> {
37 <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function declaration unsafe">unsafe_method</span>(&<span class="self_keyword">self</span>) {}
38}
39
40<span class="keyword">fn</span> <span class="function declaration">main</span>() {
41 <span class="keyword">let</span> <span class="variable declaration">x</span> = &<span class="numeric_literal">5</span> <span class="keyword">as</span> *<span class="keyword">const</span> <span class="builtin_type">usize</span>;
42 <span class="keyword unsafe">unsafe</span> {
43 <span class="function unsafe">unsafe_fn</span>();
44 <span class="struct">HasUnsafeFn</span>.<span class="function unsafe">unsafe_method</span>();
45 <span class="keyword">let</span> <span class="variable declaration">y</span> = <span class="operator unsafe">*</span><span class="variable">x</span>;
46 <span class="keyword">let</span> <span class="variable declaration">z</span> = -<span class="variable">x</span>;
47 }
48}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 352e35095..42c5f3e55 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
13.parameter { color: #94BFF3; } 14.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 15.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 16.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 2a0294f71..2dd61d20d 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -10,6 +10,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
10.string_literal { color: #CC9393; } 10.string_literal { color: #CC9393; }
11.field { color: #94BFF3; } 11.field { color: #94BFF3; }
12.function { color: #93E0E3; } 12.function { color: #93E0E3; }
13.operator.unsafe { color: #E28C14; }
13.parameter { color: #94BFF3; } 14.parameter { color: #94BFF3; }
14.text { color: #DCDCCC; } 15.text { color: #DCDCCC; }
15.type { color: #7CB8BB; } 16.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 0b53ebe69..19ecd54d6 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -406,6 +406,23 @@ fn highlight_element(
406 _ => h, 406 _ => h,
407 } 407 }
408 } 408 }
409 PREFIX_EXPR => {
410 let prefix_expr = element.into_node().and_then(ast::PrefixExpr::cast)?;
411 match prefix_expr.op_kind() {
412 Some(ast::PrefixOp::Deref) => {}
413 _ => return None,
414 }
415
416 let expr = prefix_expr.expr()?;
417 let ty = sema.type_of_expr(&expr)?;
418 if !ty.is_raw_ptr() {
419 return None;
420 }
421
422 let mut h = Highlight::new(HighlightTag::Operator);
423 h |= HighlightModifier::Unsafe;
424 h
425 }
409 426
410 k if k.is_keyword() => { 427 k if k.is_keyword() => {
411 let h = Highlight::new(HighlightTag::Keyword); 428 let h = Highlight::new(HighlightTag::Keyword);
@@ -458,7 +475,13 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
458 Definition::Field(_) => HighlightTag::Field, 475 Definition::Field(_) => HighlightTag::Field,
459 Definition::ModuleDef(def) => match def { 476 Definition::ModuleDef(def) => match def {
460 hir::ModuleDef::Module(_) => HighlightTag::Module, 477 hir::ModuleDef::Module(_) => HighlightTag::Module,
461 hir::ModuleDef::Function(_) => HighlightTag::Function, 478 hir::ModuleDef::Function(func) => {
479 let mut h = HighlightTag::Function.into();
480 if func.is_unsafe(db) {
481 h |= HighlightModifier::Unsafe;
482 }
483 return h;
484 }
462 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 485 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
463 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, 486 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum,
464 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union, 487 hir::ModuleDef::Adt(hir::Adt::Union(_)) => HighlightTag::Union,
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index edfe61f39..7d946c98d 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -69,6 +69,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
69.string_literal { color: #CC9393; } 69.string_literal { color: #CC9393; }
70.field { color: #94BFF3; } 70.field { color: #94BFF3; }
71.function { color: #93E0E3; } 71.function { color: #93E0E3; }
72.operator.unsafe { color: #E28C14; }
72.parameter { color: #94BFF3; } 73.parameter { color: #94BFF3; }
73.text { color: #DCDCCC; } 74.text { color: #DCDCCC; }
74.type { color: #7CB8BB; } 75.type { color: #7CB8BB; }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 1514531de..94f466966 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -24,12 +24,14 @@ pub enum HighlightTag {
24 Enum, 24 Enum,
25 EnumVariant, 25 EnumVariant,
26 Field, 26 Field,
27 FormatSpecifier,
27 Function, 28 Function,
28 Keyword, 29 Keyword,
29 Lifetime, 30 Lifetime,
30 Macro, 31 Macro,
31 Module, 32 Module,
32 NumericLiteral, 33 NumericLiteral,
34 Operator,
33 SelfKeyword, 35 SelfKeyword,
34 SelfType, 36 SelfType,
35 Static, 37 Static,
@@ -41,8 +43,6 @@ pub enum HighlightTag {
41 Union, 43 Union,
42 Local, 44 Local,
43 UnresolvedReference, 45 UnresolvedReference,
44 FormatSpecifier,
45 Operator,
46} 46}
47 47
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -72,12 +72,14 @@ impl HighlightTag {
72 HighlightTag::Enum => "enum", 72 HighlightTag::Enum => "enum",
73 HighlightTag::EnumVariant => "enum_variant", 73 HighlightTag::EnumVariant => "enum_variant",
74 HighlightTag::Field => "field", 74 HighlightTag::Field => "field",
75 HighlightTag::FormatSpecifier => "format_specifier",
75 HighlightTag::Function => "function", 76 HighlightTag::Function => "function",
76 HighlightTag::Keyword => "keyword", 77 HighlightTag::Keyword => "keyword",
77 HighlightTag::Lifetime => "lifetime", 78 HighlightTag::Lifetime => "lifetime",
78 HighlightTag::Macro => "macro", 79 HighlightTag::Macro => "macro",
79 HighlightTag::Module => "module", 80 HighlightTag::Module => "module",
80 HighlightTag::NumericLiteral => "numeric_literal", 81 HighlightTag::NumericLiteral => "numeric_literal",
82 HighlightTag::Operator => "operator",
81 HighlightTag::SelfKeyword => "self_keyword", 83 HighlightTag::SelfKeyword => "self_keyword",
82 HighlightTag::SelfType => "self_type", 84 HighlightTag::SelfType => "self_type",
83 HighlightTag::Static => "static", 85 HighlightTag::Static => "static",
@@ -89,8 +91,6 @@ impl HighlightTag {
89 HighlightTag::Union => "union", 91 HighlightTag::Union => "union",
90 HighlightTag::Local => "variable", 92 HighlightTag::Local => "variable",
91 HighlightTag::UnresolvedReference => "unresolved_reference", 93 HighlightTag::UnresolvedReference => "unresolved_reference",
92 HighlightTag::FormatSpecifier => "format_specifier",
93 HighlightTag::Operator => "operator",
94 } 94 }
95 } 95 }
96} 96}
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 7dc229cab..36a1aa419 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -258,3 +258,34 @@ fn main() {
258 fs::write(dst_file, &actual_html).unwrap(); 258 fs::write(dst_file, &actual_html).unwrap();
259 assert_eq_text!(expected_html, actual_html); 259 assert_eq_text!(expected_html, actual_html);
260} 260}
261
262#[test]
263fn test_unsafe_highlighting() {
264 let (analysis, file_id) = single_file(
265 r#"
266unsafe fn unsafe_fn() {}
267
268struct HasUnsafeFn;
269
270impl HasUnsafeFn {
271 unsafe fn unsafe_method(&self) {}
272}
273
274fn main() {
275 let x = &5 as *const usize;
276 unsafe {
277 unsafe_fn();
278 HasUnsafeFn.unsafe_method();
279 let y = *x;
280 let z = -x;
281 }
282}
283"#
284 .trim(),
285 );
286 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_unsafe.html");
287 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
288 let expected_html = &read_text(&dst_file);
289 fs::write(dst_file, &actual_html).unwrap();
290 assert_eq_text!(expected_html, actual_html);
291}
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs
index be0cd5661..293baecf6 100644
--- a/crates/ra_parser/src/grammar.rs
+++ b/crates/ra_parser/src/grammar.rs
@@ -18,9 +18,10 @@
18//! // fn foo() {} 18//! // fn foo() {}
19//! ``` 19//! ```
20//! 20//!
21//! After adding a new inline-test, run `cargo collect-tests` to extract 21//! After adding a new inline-test, run `cargo xtask codegen` to
22//! it as a standalone text-fixture into `tests/data/parser/inline`, and 22//! extract it as a standalone text-fixture into
23//! run `cargo test` once to create the "gold" value. 23//! `crates/ra_syntax/test_data/parser/`, and run `cargo test` once to
24//! create the "gold" value.
24//! 25//!
25//! Coding convention: rules like `where_clause` always produce either a 26//! Coding convention: rules like `where_clause` always produce either a
26//! node or an error, rules like `opt_where_clause` may produce nothing. 27//! node or an error, rules like `opt_where_clause` may produce nothing.
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs
index b030c8a6a..09c06fef9 100644
--- a/crates/ra_project_model/src/json_project.rs
+++ b/crates/ra_project_model/src/json_project.rs
@@ -5,6 +5,13 @@ use std::path::PathBuf;
5use rustc_hash::{FxHashMap, FxHashSet}; 5use rustc_hash::{FxHashMap, FxHashSet};
6use serde::Deserialize; 6use serde::Deserialize;
7 7
8/// Roots and crates that compose this Rust project.
9#[derive(Clone, Debug, Deserialize)]
10pub struct JsonProject {
11 pub(crate) roots: Vec<Root>,
12 pub(crate) crates: Vec<Crate>,
13}
14
8/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in 15/// A root points to the directory which contains Rust crates. rust-analyzer watches all files in
9/// all roots. Roots might be nested. 16/// all roots. Roots might be nested.
10#[derive(Clone, Debug, Deserialize)] 17#[derive(Clone, Debug, Deserialize)]
@@ -20,8 +27,17 @@ pub struct Crate {
20 pub(crate) root_module: PathBuf, 27 pub(crate) root_module: PathBuf,
21 pub(crate) edition: Edition, 28 pub(crate) edition: Edition,
22 pub(crate) deps: Vec<Dep>, 29 pub(crate) deps: Vec<Dep>,
30
31 // This is the preferred method of providing cfg options.
32 #[serde(default)]
33 pub(crate) cfg: FxHashSet<String>,
34
35 // These two are here for transition only.
36 #[serde(default)]
23 pub(crate) atom_cfgs: FxHashSet<String>, 37 pub(crate) atom_cfgs: FxHashSet<String>,
38 #[serde(default)]
24 pub(crate) key_value_cfgs: FxHashMap<String, String>, 39 pub(crate) key_value_cfgs: FxHashMap<String, String>,
40
25 pub(crate) out_dir: Option<PathBuf>, 41 pub(crate) out_dir: Option<PathBuf>,
26 pub(crate) proc_macro_dylib_path: Option<PathBuf>, 42 pub(crate) proc_macro_dylib_path: Option<PathBuf>,
27} 43}
@@ -48,9 +64,72 @@ pub struct Dep {
48 pub(crate) name: String, 64 pub(crate) name: String,
49} 65}
50 66
51/// Roots and crates that compose this Rust project. 67#[cfg(test)]
52#[derive(Clone, Debug, Deserialize)] 68mod tests {
53pub struct JsonProject { 69 use super::*;
54 pub(crate) roots: Vec<Root>, 70 use serde_json::json;
55 pub(crate) crates: Vec<Crate>, 71
72 #[test]
73 fn test_crate_deserialization() {
74 let raw_json = json!( {
75 "crate_id": 2,
76 "root_module": "this/is/a/file/path.rs",
77 "deps": [
78 {
79 "crate": 1,
80 "name": "some_dep_crate"
81 },
82 ],
83 "edition": "2015",
84 "cfg": [
85 "atom_1",
86 "atom_2",
87 "feature=feature_1",
88 "feature=feature_2",
89 "other=value",
90 ],
91
92 });
93
94 let krate: Crate = serde_json::from_value(raw_json).unwrap();
95
96 assert!(krate.cfg.contains(&"atom_1".to_string()));
97 assert!(krate.cfg.contains(&"atom_2".to_string()));
98 assert!(krate.cfg.contains(&"feature=feature_1".to_string()));
99 assert!(krate.cfg.contains(&"feature=feature_2".to_string()));
100 assert!(krate.cfg.contains(&"other=value".to_string()));
101 }
102
103 #[test]
104 fn test_crate_deserialization_old_json() {
105 let raw_json = json!( {
106 "crate_id": 2,
107 "root_module": "this/is/a/file/path.rs",
108 "deps": [
109 {
110 "crate": 1,
111 "name": "some_dep_crate"
112 },
113 ],
114 "edition": "2015",
115 "atom_cfgs": [
116 "atom_1",
117 "atom_2",
118 ],
119 "key_value_cfgs": {
120 "feature": "feature_1",
121 "feature": "feature_2",
122 "other": "value",
123 },
124 });
125
126 let krate: Crate = serde_json::from_value(raw_json).unwrap();
127
128 assert!(krate.atom_cfgs.contains(&"atom_1".to_string()));
129 assert!(krate.atom_cfgs.contains(&"atom_2".to_string()));
130 assert!(krate.key_value_cfgs.contains_key(&"feature".to_string()));
131 assert_eq!(krate.key_value_cfgs.get("feature"), Some(&"feature_2".to_string()));
132 assert!(krate.key_value_cfgs.contains_key(&"other".to_string()));
133 assert_eq!(krate.key_value_cfgs.get("other"), Some(&"value".to_string()));
134 }
56} 135}
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index a2e9f65ef..7ad941279 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::{
@@ -32,6 +32,12 @@ pub enum ProjectWorkspace {
32 Json { project: JsonProject }, 32 Json { project: JsonProject },
33} 33}
34 34
35impl From<JsonProject> for ProjectWorkspace {
36 fn from(project: JsonProject) -> ProjectWorkspace {
37 ProjectWorkspace::Json { project }
38 }
39}
40
35/// `PackageRoot` describes a package root folder. 41/// `PackageRoot` describes a package root folder.
36/// Which may be an external dependency, or a member of 42/// Which may be an external dependency, or a member of
37/// the current workspace. 43/// the current workspace.
@@ -57,25 +63,25 @@ impl PackageRoot {
57 } 63 }
58} 64}
59 65
60#[derive(Debug, Clone, PartialEq, Eq, Hash)] 66#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
61pub enum ProjectRoot { 67pub enum ProjectManifest {
62 ProjectJson(PathBuf), 68 ProjectJson(PathBuf),
63 CargoToml(PathBuf), 69 CargoToml(PathBuf),
64} 70}
65 71
66impl ProjectRoot { 72impl ProjectManifest {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> { 73 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> {
68 if path.ends_with("rust-project.json") { 74 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path)); 75 return Ok(ProjectManifest::ProjectJson(path));
70 } 76 }
71 if path.ends_with("Cargo.toml") { 77 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path)); 78 return Ok(ProjectManifest::CargoToml(path));
73 } 79 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) 80 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
75 } 81 }
76 82
77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> { 83 pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
78 let mut candidates = ProjectRoot::discover(path)?; 84 let mut candidates = ProjectManifest::discover(path)?;
79 let res = match candidates.pop() { 85 let res = match candidates.pop() {
80 None => bail!("no projects"), 86 None => bail!("no projects"),
81 Some(it) => it, 87 Some(it) => it,
@@ -87,12 +93,12 @@ impl ProjectRoot {
87 Ok(res) 93 Ok(res)
88 } 94 }
89 95
90 pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { 96 pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> {
91 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { 97 if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]); 98 return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
93 } 99 }
94 return find_cargo_toml(path) 100 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); 101 .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
96 102
97 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { 103 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
98 match find_in_parent_dirs(path, "Cargo.toml") { 104 match find_in_parent_dirs(path, "Cargo.toml") {
@@ -128,16 +134,28 @@ impl ProjectRoot {
128 .collect() 134 .collect()
129 } 135 }
130 } 136 }
137
138 pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> {
139 let mut res = paths
140 .iter()
141 .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
142 .flatten()
143 .collect::<FxHashSet<_>>()
144 .into_iter()
145 .collect::<Vec<_>>();
146 res.sort();
147 res
148 }
131} 149}
132 150
133impl ProjectWorkspace { 151impl ProjectWorkspace {
134 pub fn load( 152 pub fn load(
135 root: ProjectRoot, 153 manifest: ProjectManifest,
136 cargo_features: &CargoConfig, 154 cargo_features: &CargoConfig,
137 with_sysroot: bool, 155 with_sysroot: bool,
138 ) -> Result<ProjectWorkspace> { 156 ) -> Result<ProjectWorkspace> {
139 let res = match root { 157 let res = match manifest {
140 ProjectRoot::ProjectJson(project_json) => { 158 ProjectManifest::ProjectJson(project_json) => {
141 let file = File::open(&project_json).with_context(|| { 159 let file = File::open(&project_json).with_context(|| {
142 format!("Failed to open json file {}", project_json.display()) 160 format!("Failed to open json file {}", project_json.display())
143 })?; 161 })?;
@@ -148,7 +166,7 @@ impl ProjectWorkspace {
148 })?, 166 })?,
149 } 167 }
150 } 168 }
151 ProjectRoot::CargoToml(cargo_toml) => { 169 ProjectManifest::CargoToml(cargo_toml) => {
152 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 170 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
153 .with_context(|| { 171 .with_context(|| {
154 format!( 172 format!(
@@ -252,6 +270,16 @@ impl ProjectWorkspace {
252 }; 270 };
253 let cfg_options = { 271 let cfg_options = {
254 let mut opts = default_cfg_options.clone(); 272 let mut opts = default_cfg_options.clone();
273 for cfg in &krate.cfg {
274 match cfg.find('=') {
275 None => opts.insert_atom(cfg.into()),
276 Some(pos) => {
277 let key = &cfg[..pos];
278 let value = cfg[pos + 1..].trim_matches('"');
279 opts.insert_key_value(key.into(), value.into());
280 }
281 }
282 }
255 for name in &krate.atom_cfgs { 283 for name in &krate.atom_cfgs {
256 opts.insert_atom(name.into()); 284 opts.insert_atom(name.into());
257 } 285 }
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs
index bfc05e08b..a8f2454fd 100644
--- a/crates/ra_syntax/src/ast/traits.rs
+++ b/crates/ra_syntax/src/ast/traits.rs
@@ -83,13 +83,22 @@ pub trait DocCommentsOwner: AstNode {
83 CommentIter { iter: self.syntax().children_with_tokens() } 83 CommentIter { iter: self.syntax().children_with_tokens() }
84 } 84 }
85 85
86 fn doc_comment_text(&self) -> Option<String> {
87 self.doc_comments().doc_comment_text()
88 }
89}
90
91impl CommentIter {
92 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
93 CommentIter { iter: syntax_node.children_with_tokens() }
94 }
95
86 /// Returns the textual content of a doc comment block as a single string. 96 /// Returns the textual content of a doc comment block as a single string.
87 /// That is, strips leading `///` (+ optional 1 character of whitespace), 97 /// That is, strips leading `///` (+ optional 1 character of whitespace),
88 /// trailing `*/`, trailing whitespace and then joins the lines. 98 /// trailing `*/`, trailing whitespace and then joins the lines.
89 fn doc_comment_text(&self) -> Option<String> { 99 pub fn doc_comment_text(self) -> Option<String> {
90 let mut has_comments = false; 100 let mut has_comments = false;
91 let docs = self 101 let docs = self
92 .doc_comments()
93 .filter(|comment| comment.kind().doc.is_some()) 102 .filter(|comment| comment.kind().doc.is_some())
94 .map(|comment| { 103 .map(|comment| {
95 has_comments = true; 104 has_comments = true;
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/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 008518a08..44f856f6b 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -4,7 +4,7 @@ use ra_cfg::CfgExpr;
4use ra_ide::{FileId, RunnableKind, TestId}; 4use ra_ide::{FileId, RunnableKind, TestId};
5use ra_project_model::{self, ProjectWorkspace, TargetKind}; 5use ra_project_model::{self, ProjectWorkspace, TargetKind};
6 6
7use crate::{world::WorldSnapshot, Result}; 7use crate::{global_state::GlobalStateSnapshot, Result};
8 8
9/// Abstract representation of Cargo target. 9/// Abstract representation of Cargo target.
10/// 10///
@@ -89,7 +89,7 @@ impl CargoTargetSpec {
89 } 89 }
90 90
91 pub(crate) fn for_file( 91 pub(crate) fn for_file(
92 world: &WorldSnapshot, 92 world: &GlobalStateSnapshot,
93 file_id: FileId, 93 file_id: FileId,
94 ) -> Result<Option<CargoTargetSpec>> { 94 ) -> Result<Option<CargoTargetSpec>> {
95 let &crate_id = match world.analysis().crate_for(file_id)?.first() { 95 let &crate_id = match world.analysis().crate_for(file_id)?.first() {
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 3337078ac..23168c3ae 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)]
@@ -142,6 +162,7 @@ impl Default for Config {
142 assist: AssistConfig::default(), 162 assist: AssistConfig::default(),
143 call_info_full: true, 163 call_info_full: true,
144 lens: LensConfig::default(), 164 lens: LensConfig::default(),
165 linked_projects: Vec::new(),
145 } 166 }
146 } 167 }
147} 168}
@@ -241,6 +262,22 @@ impl Config {
241 self.lens = LensConfig::NO_LENS; 262 self.lens = LensConfig::NO_LENS;
242 } 263 }
243 264
265 if let Some(linked_projects) = get::<Vec<ManifestOrJsonProject>>(value, "/linkedProjects") {
266 if !linked_projects.is_empty() {
267 self.linked_projects.clear();
268 for linked_project in linked_projects {
269 let linked_project = match linked_project {
270 ManifestOrJsonProject::Manifest(it) => match ProjectManifest::from_manifest_file(it) {
271 Ok(it) => it.into(),
272 Err(_) => continue,
273 }
274 ManifestOrJsonProject::JsonProject(it) => it.into(),
275 };
276 self.linked_projects.push(linked_project);
277 }
278 }
279 }
280
244 log::info!("Config::update() = {:#?}", self); 281 log::info!("Config::update() = {:#?}", self);
245 282
246 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { 283 fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> {
@@ -308,3 +345,10 @@ impl Config {
308 } 345 }
309 } 346 }
310} 347}
348
349#[derive(Deserialize)]
350#[serde(untagged)]
351enum ManifestOrJsonProject {
352 Manifest(PathBuf),
353 JsonProject(JsonProject),
354}
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
index 9a7972ff5..f0273315e 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -29,7 +29,7 @@ expression: diag
29 }, 29 },
30 }, 30 },
31 severity: Some( 31 severity: Some(
32 Warning, 32 Hint,
33 ), 33 ),
34 code: Some( 34 code: Some(
35 String( 35 String(
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 257910e09..04e286780 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -184,7 +184,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
184 return Vec::new(); 184 return Vec::new();
185 } 185 }
186 186
187 let severity = map_level_to_severity(rd.level); 187 let mut severity = map_level_to_severity(rd.level);
188 188
189 let mut source = String::from("rustc"); 189 let mut source = String::from("rustc");
190 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 190 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -226,6 +226,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
226 } 226 }
227 227
228 if is_unused_or_unnecessary(rd) { 228 if is_unused_or_unnecessary(rd) {
229 severity = Some(DiagnosticSeverity::Hint);
229 tags.push(DiagnosticTag::Unnecessary); 230 tags.push(DiagnosticTag::Unnecessary);
230 } 231 }
231 232
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs
index 4bb16a496..206673829 100644
--- a/crates/rust-analyzer/src/from_proto.rs
+++ b/crates/rust-analyzer/src/from_proto.rs
@@ -3,7 +3,7 @@ use ra_db::{FileId, FilePosition, FileRange};
3use ra_ide::{LineCol, LineIndex}; 3use ra_ide::{LineCol, LineIndex};
4use ra_syntax::{TextRange, TextSize}; 4use ra_syntax::{TextRange, TextSize};
5 5
6use crate::{world::WorldSnapshot, Result}; 6use crate::{global_state::GlobalStateSnapshot, Result};
7 7
8pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { 8pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize {
9 let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 }; 9 let line_col = LineCol { line: position.line as u32, col_utf16: position.character as u32 };
@@ -16,12 +16,12 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Tex
16 TextRange::new(start, end) 16 TextRange::new(start, end)
17} 17}
18 18
19pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result<FileId> { 19pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> {
20 world.uri_to_file_id(url) 20 world.uri_to_file_id(url)
21} 21}
22 22
23pub(crate) fn file_position( 23pub(crate) fn file_position(
24 world: &WorldSnapshot, 24 world: &GlobalStateSnapshot,
25 tdpp: lsp_types::TextDocumentPositionParams, 25 tdpp: lsp_types::TextDocumentPositionParams,
26) -> Result<FilePosition> { 26) -> Result<FilePosition> {
27 let file_id = file_id(world, &tdpp.text_document.uri)?; 27 let file_id = file_id(world, &tdpp.text_document.uri)?;
@@ -31,7 +31,7 @@ pub(crate) fn file_position(
31} 31}
32 32
33pub(crate) fn file_range( 33pub(crate) fn file_range(
34 world: &WorldSnapshot, 34 world: &GlobalStateSnapshot,
35 text_document_identifier: lsp_types::TextDocumentIdentifier, 35 text_document_identifier: lsp_types::TextDocumentIdentifier,
36 range: lsp_types::Range, 36 range: lsp_types::Range,
37) -> Result<FileRange> { 37) -> Result<FileRange> {
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/global_state.rs
index 367272925..0bebb5bf6 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -50,15 +50,15 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) ->
50 }) 50 })
51} 51}
52 52
53/// `WorldState` is the primary mutable state of the language server 53/// `GlobalState` is the primary mutable state of the language server
54/// 54///
55/// The most interesting components are `vfs`, which stores a consistent 55/// The most interesting components are `vfs`, which stores a consistent
56/// snapshot of the file systems, and `analysis_host`, which stores our 56/// snapshot of the file systems, and `analysis_host`, which stores our
57/// incremental salsa database. 57/// incremental salsa database.
58#[derive(Debug)] 58#[derive(Debug)]
59pub struct WorldState { 59pub struct GlobalState {
60 pub config: Config, 60 pub config: Config,
61 pub roots: Vec<PathBuf>, 61 pub local_roots: Vec<PathBuf>,
62 pub workspaces: Arc<Vec<ProjectWorkspace>>, 62 pub workspaces: Arc<Vec<ProjectWorkspace>>,
63 pub analysis_host: AnalysisHost, 63 pub analysis_host: AnalysisHost,
64 pub vfs: Arc<RwLock<Vfs>>, 64 pub vfs: Arc<RwLock<Vfs>>,
@@ -70,7 +70,7 @@ pub struct WorldState {
70} 70}
71 71
72/// An immutable snapshot of the world's state at a point in time. 72/// An immutable snapshot of the world's state at a point in time.
73pub struct WorldSnapshot { 73pub struct GlobalStateSnapshot {
74 pub config: Config, 74 pub config: Config,
75 pub workspaces: Arc<Vec<ProjectWorkspace>>, 75 pub workspaces: Arc<Vec<ProjectWorkspace>>,
76 pub analysis: Analysis, 76 pub analysis: Analysis,
@@ -79,20 +79,20 @@ pub struct WorldSnapshot {
79 vfs: Arc<RwLock<Vfs>>, 79 vfs: Arc<RwLock<Vfs>>,
80} 80}
81 81
82impl WorldState { 82impl GlobalState {
83 pub fn new( 83 pub fn new(
84 folder_roots: Vec<PathBuf>,
85 workspaces: Vec<ProjectWorkspace>, 84 workspaces: Vec<ProjectWorkspace>,
86 lru_capacity: Option<usize>, 85 lru_capacity: Option<usize>,
87 exclude_globs: &[Glob], 86 exclude_globs: &[Glob],
88 watch: Watch, 87 watch: Watch,
89 config: Config, 88 config: Config,
90 ) -> WorldState { 89 ) -> GlobalState {
91 let mut change = AnalysisChange::new(); 90 let mut change = AnalysisChange::new();
92 91
93 let extern_dirs: FxHashSet<_> = 92 let extern_dirs: FxHashSet<_> =
94 workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect(); 93 workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect();
95 94
95 let mut local_roots = Vec::new();
96 let roots: Vec<_> = { 96 let roots: Vec<_> = {
97 let create_filter = |is_member| { 97 let create_filter = |is_member| {
98 RustPackageFilterBuilder::default() 98 RustPackageFilterBuilder::default()
@@ -100,12 +100,16 @@ impl WorldState {
100 .exclude(exclude_globs.iter().cloned()) 100 .exclude(exclude_globs.iter().cloned())
101 .into_vfs_filter() 101 .into_vfs_filter()
102 }; 102 };
103 folder_roots 103 workspaces
104 .iter() 104 .iter()
105 .map(|path| RootEntry::new(path.clone(), create_filter(true))) 105 .flat_map(ProjectWorkspace::to_roots)
106 .chain(workspaces.iter().flat_map(ProjectWorkspace::to_roots).map(|pkg_root| { 106 .map(|pkg_root| {
107 RootEntry::new(pkg_root.path().to_owned(), create_filter(pkg_root.is_member())) 107 let path = pkg_root.path().to_owned();
108 })) 108 if pkg_root.is_member() {
109 local_roots.push(path.clone());
110 }
111 RootEntry::new(path, create_filter(pkg_root.is_member()))
112 })
109 .chain( 113 .chain(
110 extern_dirs 114 extern_dirs
111 .iter() 115 .iter()
@@ -121,7 +125,7 @@ impl WorldState {
121 let mut extern_source_roots = FxHashMap::default(); 125 let mut extern_source_roots = FxHashMap::default();
122 for r in vfs_roots { 126 for r in vfs_roots {
123 let vfs_root_path = vfs.root2path(r); 127 let vfs_root_path = vfs.root2path(r);
124 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); 128 let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it));
125 change.add_root(SourceRootId(r.0), is_local); 129 change.add_root(SourceRootId(r.0), is_local);
126 change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); 130 change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());
127 131
@@ -176,9 +180,9 @@ impl WorldState {
176 180
177 let mut analysis_host = AnalysisHost::new(lru_capacity); 181 let mut analysis_host = AnalysisHost::new(lru_capacity);
178 analysis_host.apply_change(change); 182 analysis_host.apply_change(change);
179 WorldState { 183 GlobalState {
180 config, 184 config,
181 roots: folder_roots, 185 local_roots,
182 workspaces: Arc::new(workspaces), 186 workspaces: Arc::new(workspaces),
183 analysis_host, 187 analysis_host,
184 vfs: Arc::new(RwLock::new(vfs)), 188 vfs: Arc::new(RwLock::new(vfs)),
@@ -216,7 +220,7 @@ impl WorldState {
216 match c { 220 match c {
217 VfsChange::AddRoot { root, files } => { 221 VfsChange::AddRoot { root, files } => {
218 let root_path = self.vfs.read().root2path(root); 222 let root_path = self.vfs.read().root2path(root);
219 let is_local = self.roots.iter().any(|r| root_path.starts_with(r)); 223 let is_local = self.local_roots.iter().any(|r| root_path.starts_with(r));
220 if is_local { 224 if is_local {
221 *roots_scanned += 1; 225 *roots_scanned += 1;
222 for (file, path, text) in files { 226 for (file, path, text) in files {
@@ -251,8 +255,8 @@ impl WorldState {
251 self.analysis_host.apply_change(change); 255 self.analysis_host.apply_change(change);
252 } 256 }
253 257
254 pub fn snapshot(&self) -> WorldSnapshot { 258 pub fn snapshot(&self) -> GlobalStateSnapshot {
255 WorldSnapshot { 259 GlobalStateSnapshot {
256 config: self.config.clone(), 260 config: self.config.clone(),
257 workspaces: Arc::clone(&self.workspaces), 261 workspaces: Arc::clone(&self.workspaces),
258 analysis: self.analysis_host.analysis(), 262 analysis: self.analysis_host.analysis(),
@@ -275,7 +279,7 @@ impl WorldState {
275 } 279 }
276} 280}
277 281
278impl WorldSnapshot { 282impl GlobalStateSnapshot {
279 pub fn analysis(&self) -> &Analysis { 283 pub fn analysis(&self) -> &Analysis {
280 &self.analysis 284 &self.analysis
281 } 285 }
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 57d0e9218..609cb69d3 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -26,7 +26,7 @@ mod main_loop;
26mod markdown; 26mod markdown;
27pub mod lsp_ext; 27pub mod lsp_ext;
28pub mod config; 28pub mod config;
29mod world; 29mod global_state;
30mod diagnostics; 30mod diagnostics;
31mod semantic_tokens; 31mod semantic_tokens;
32 32
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index ad9dd4c59..e60337b8e 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,14 +34,15 @@ 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, lsp_ext, 39 from_proto,
40 global_state::{GlobalState, GlobalStateSnapshot},
41 lsp_ext,
42 main_loop::{ 42 main_loop::{
43 pending_requests::{PendingRequest, PendingRequests}, 43 pending_requests::{PendingRequest, PendingRequests},
44 subscriptions::Subscriptions, 44 subscriptions::Subscriptions,
45 }, 45 },
46 world::{WorldSnapshot, WorldState},
47 Result, 46 Result,
48}; 47};
49 48
@@ -69,7 +68,7 @@ impl fmt::Display for LspError {
69 68
70impl Error for LspError {} 69impl Error for LspError {}
71 70
72pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> { 71pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
73 log::info!("initial config: {:#?}", config); 72 log::info!("initial config: {:#?}", config);
74 73
75 // Windows scheduler implements priority boosts: if thread waits for an 74 // Windows scheduler implements priority boosts: if thread waits for an
@@ -92,43 +91,37 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
92 } 91 }
93 92
94 let mut loop_state = LoopState::default(); 93 let mut loop_state = LoopState::default();
95 let mut world_state = { 94 let mut global_state = {
96 let workspaces = { 95 let workspaces = {
97 // FIXME: support dynamic workspace loading. 96 if config.linked_projects.is_empty() && config.notifications.cargo_toml_not_found {
98 let project_roots: FxHashSet<_> = ws_roots
99 .iter()
100 .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
101 .flatten()
102 .collect();
103
104 if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
105 show_message( 97 show_message(
106 lsp_types::MessageType::Error, 98 lsp_types::MessageType::Error,
107 format!( 99 "rust-analyzer failed to discover workspace".to_string(),
108 "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
109 ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
110 ),
111 &connection.sender, 100 &connection.sender,
112 ); 101 );
113 }; 102 };
114 103
115 project_roots 104 config
116 .into_iter() 105 .linked_projects
117 .filter_map(|root| { 106 .iter()
118 ra_project_model::ProjectWorkspace::load( 107 .filter_map(|project| match project {
119 root, 108 LinkedProject::ProjectManifest(manifest) => {
120 &config.cargo, 109 ra_project_model::ProjectWorkspace::load(
121 config.with_sysroot, 110 manifest.clone(),
122 ) 111 &config.cargo,
123 .map_err(|err| { 112 config.with_sysroot,
124 log::error!("failed to load workspace: {:#}", err); 113 )
125 show_message( 114 .map_err(|err| {
126 lsp_types::MessageType::Error, 115 log::error!("failed to load workspace: {:#}", err);
127 format!("rust-analyzer failed to load workspace: {:#}", err), 116 show_message(
128 &connection.sender, 117 lsp_types::MessageType::Error,
129 ); 118 format!("rust-analyzer failed to load workspace: {:#}", err),
130 }) 119 &connection.sender,
131 .ok() 120 );
121 })
122 .ok()
123 }
124 LinkedProject::JsonProject(it) => Some(it.clone().into()),
132 }) 125 })
133 .collect::<Vec<_>>() 126 .collect::<Vec<_>>()
134 }; 127 };
@@ -163,8 +156,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
163 connection.sender.send(request.into()).unwrap(); 156 connection.sender.send(request.into()).unwrap();
164 } 157 }
165 158
166 WorldState::new( 159 GlobalState::new(
167 ws_roots,
168 workspaces, 160 workspaces,
169 config.lru_capacity, 161 config.lru_capacity,
170 &globs, 162 &globs,
@@ -173,7 +165,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
173 ) 165 )
174 }; 166 };
175 167
176 loop_state.roots_total = world_state.vfs.read().n_roots(); 168 loop_state.roots_total = global_state.vfs.read().n_roots();
177 169
178 let pool = ThreadPool::default(); 170 let pool = ThreadPool::default();
179 let (task_sender, task_receiver) = unbounded::<Task>(); 171 let (task_sender, task_receiver) = unbounded::<Task>();
@@ -191,12 +183,12 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
191 Err(RecvError) => return Err("client exited without shutdown".into()), 183 Err(RecvError) => return Err("client exited without shutdown".into()),
192 }, 184 },
193 recv(task_receiver) -> task => Event::Task(task.unwrap()), 185 recv(task_receiver) -> task => Event::Task(task.unwrap()),
194 recv(world_state.task_receiver) -> task => match task { 186 recv(global_state.task_receiver) -> task => match task {
195 Ok(task) => Event::Vfs(task), 187 Ok(task) => Event::Vfs(task),
196 Err(RecvError) => return Err("vfs died".into()), 188 Err(RecvError) => return Err("vfs died".into()),
197 }, 189 },
198 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()), 190 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()),
199 recv(world_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { 191 recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task {
200 Ok(task) => Event::CheckWatcher(task), 192 Ok(task) => Event::CheckWatcher(task),
201 Err(RecvError) => return Err("check watcher died".into()), 193 Err(RecvError) => return Err("check watcher died".into()),
202 } 194 }
@@ -211,16 +203,16 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
211 &task_sender, 203 &task_sender,
212 &libdata_sender, 204 &libdata_sender,
213 &connection, 205 &connection,
214 &mut world_state, 206 &mut global_state,
215 &mut loop_state, 207 &mut loop_state,
216 event, 208 event,
217 )?; 209 )?;
218 } 210 }
219 } 211 }
220 world_state.analysis_host.request_cancellation(); 212 global_state.analysis_host.request_cancellation();
221 log::info!("waiting for tasks to finish..."); 213 log::info!("waiting for tasks to finish...");
222 task_receiver.into_iter().for_each(|task| { 214 task_receiver.into_iter().for_each(|task| {
223 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut world_state) 215 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut global_state)
224 }); 216 });
225 libdata_receiver.into_iter().for_each(drop); 217 libdata_receiver.into_iter().for_each(drop);
226 log::info!("...tasks have finished"); 218 log::info!("...tasks have finished");
@@ -229,7 +221,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
229 drop(pool); 221 drop(pool);
230 log::info!("...threadpool has finished"); 222 log::info!("...threadpool has finished");
231 223
232 let vfs = Arc::try_unwrap(world_state.vfs).expect("all snapshots should be dead"); 224 let vfs = Arc::try_unwrap(global_state.vfs).expect("all snapshots should be dead");
233 drop(vfs); 225 drop(vfs);
234 226
235 Ok(()) 227 Ok(())
@@ -320,7 +312,7 @@ fn loop_turn(
320 task_sender: &Sender<Task>, 312 task_sender: &Sender<Task>,
321 libdata_sender: &Sender<LibraryData>, 313 libdata_sender: &Sender<LibraryData>,
322 connection: &Connection, 314 connection: &Connection,
323 world_state: &mut WorldState, 315 global_state: &mut GlobalState,
324 loop_state: &mut LoopState, 316 loop_state: &mut LoopState,
325 event: Event, 317 event: Event,
326) -> Result<()> { 318) -> Result<()> {
@@ -336,22 +328,22 @@ fn loop_turn(
336 328
337 match event { 329 match event {
338 Event::Task(task) => { 330 Event::Task(task) => {
339 on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state); 331 on_task(task, &connection.sender, &mut loop_state.pending_requests, global_state);
340 world_state.maybe_collect_garbage(); 332 global_state.maybe_collect_garbage();
341 } 333 }
342 Event::Vfs(task) => { 334 Event::Vfs(task) => {
343 world_state.vfs.write().handle_task(task); 335 global_state.vfs.write().handle_task(task);
344 } 336 }
345 Event::Lib(lib) => { 337 Event::Lib(lib) => {
346 world_state.add_lib(lib); 338 global_state.add_lib(lib);
347 world_state.maybe_collect_garbage(); 339 global_state.maybe_collect_garbage();
348 loop_state.in_flight_libraries -= 1; 340 loop_state.in_flight_libraries -= 1;
349 loop_state.roots_scanned += 1; 341 loop_state.roots_scanned += 1;
350 } 342 }
351 Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?, 343 Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?,
352 Event::Msg(msg) => match msg { 344 Event::Msg(msg) => match msg {
353 Message::Request(req) => on_request( 345 Message::Request(req) => on_request(
354 world_state, 346 global_state,
355 &mut loop_state.pending_requests, 347 &mut loop_state.pending_requests,
356 pool, 348 pool,
357 task_sender, 349 task_sender,
@@ -360,7 +352,7 @@ fn loop_turn(
360 req, 352 req,
361 )?, 353 )?,
362 Message::Notification(not) => { 354 Message::Notification(not) => {
363 on_notification(&connection.sender, world_state, loop_state, not)?; 355 on_notification(&connection.sender, global_state, loop_state, not)?;
364 } 356 }
365 Message::Response(resp) => { 357 Message::Response(resp) => {
366 let removed = loop_state.pending_responses.remove(&resp.id); 358 let removed = loop_state.pending_responses.remove(&resp.id);
@@ -379,9 +371,9 @@ fn loop_turn(
379 } 371 }
380 (None, Some(configs)) => { 372 (None, Some(configs)) => {
381 if let Some(new_config) = configs.get(0) { 373 if let Some(new_config) = configs.get(0) {
382 let mut config = world_state.config.clone(); 374 let mut config = global_state.config.clone();
383 config.update(&new_config); 375 config.update(&new_config);
384 world_state.update_configuration(config); 376 global_state.update_configuration(config);
385 } 377 }
386 } 378 }
387 (None, None) => { 379 (None, None) => {
@@ -394,7 +386,7 @@ fn loop_turn(
394 }; 386 };
395 387
396 let mut state_changed = false; 388 let mut state_changed = false;
397 if let Some(changes) = world_state.process_changes(&mut loop_state.roots_scanned) { 389 if let Some(changes) = global_state.process_changes(&mut loop_state.roots_scanned) {
398 state_changed = true; 390 state_changed = true;
399 loop_state.pending_libraries.extend(changes); 391 loop_state.pending_libraries.extend(changes);
400 } 392 }
@@ -416,7 +408,7 @@ fn loop_turn(
416 } 408 }
417 409
418 let show_progress = 410 let show_progress =
419 !loop_state.workspace_loaded && world_state.config.client_caps.work_done_progress; 411 !loop_state.workspace_loaded && global_state.config.client_caps.work_done_progress;
420 412
421 if !loop_state.workspace_loaded 413 if !loop_state.workspace_loaded
422 && loop_state.roots_scanned == loop_state.roots_total 414 && loop_state.roots_scanned == loop_state.roots_total
@@ -425,7 +417,7 @@ fn loop_turn(
425 { 417 {
426 state_changed = true; 418 state_changed = true;
427 loop_state.workspace_loaded = true; 419 loop_state.workspace_loaded = true;
428 if let Some(flycheck) = &world_state.flycheck { 420 if let Some(flycheck) = &global_state.flycheck {
429 flycheck.update(); 421 flycheck.update();
430 } 422 }
431 } 423 }
@@ -437,13 +429,13 @@ fn loop_turn(
437 if state_changed && loop_state.workspace_loaded { 429 if state_changed && loop_state.workspace_loaded {
438 update_file_notifications_on_threadpool( 430 update_file_notifications_on_threadpool(
439 pool, 431 pool,
440 world_state.snapshot(), 432 global_state.snapshot(),
441 task_sender.clone(), 433 task_sender.clone(),
442 loop_state.subscriptions.subscriptions(), 434 loop_state.subscriptions.subscriptions(),
443 ); 435 );
444 pool.execute({ 436 pool.execute({
445 let subs = loop_state.subscriptions.subscriptions(); 437 let subs = loop_state.subscriptions.subscriptions();
446 let snap = world_state.snapshot(); 438 let snap = global_state.snapshot();
447 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) 439 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
448 }); 440 });
449 } 441 }
@@ -467,7 +459,7 @@ fn on_task(
467 task: Task, 459 task: Task,
468 msg_sender: &Sender<Message>, 460 msg_sender: &Sender<Message>,
469 pending_requests: &mut PendingRequests, 461 pending_requests: &mut PendingRequests,
470 state: &mut WorldState, 462 state: &mut GlobalState,
471) { 463) {
472 match task { 464 match task {
473 Task::Respond(response) => { 465 Task::Respond(response) => {
@@ -485,7 +477,7 @@ fn on_task(
485} 477}
486 478
487fn on_request( 479fn on_request(
488 world: &mut WorldState, 480 global_state: &mut GlobalState,
489 pending_requests: &mut PendingRequests, 481 pending_requests: &mut PendingRequests,
490 pool: &ThreadPool, 482 pool: &ThreadPool,
491 task_sender: &Sender<Task>, 483 task_sender: &Sender<Task>,
@@ -496,7 +488,7 @@ fn on_request(
496 let mut pool_dispatcher = PoolDispatcher { 488 let mut pool_dispatcher = PoolDispatcher {
497 req: Some(req), 489 req: Some(req),
498 pool, 490 pool,
499 world, 491 global_state,
500 task_sender, 492 task_sender,
501 msg_sender, 493 msg_sender,
502 pending_requests, 494 pending_requests,
@@ -553,7 +545,7 @@ fn on_request(
553 545
554fn on_notification( 546fn on_notification(
555 msg_sender: &Sender<Message>, 547 msg_sender: &Sender<Message>,
556 state: &mut WorldState, 548 state: &mut GlobalState,
557 loop_state: &mut LoopState, 549 loop_state: &mut LoopState,
558 not: Notification, 550 not: Notification,
559) -> Result<()> { 551) -> Result<()> {
@@ -727,7 +719,7 @@ fn apply_document_changes(
727 719
728fn on_check_task( 720fn on_check_task(
729 task: CheckTask, 721 task: CheckTask,
730 world_state: &mut WorldState, 722 global_state: &mut GlobalState,
731 task_sender: &Sender<Task>, 723 task_sender: &Sender<Task>,
732) -> Result<()> { 724) -> Result<()> {
733 match task { 725 match task {
@@ -746,7 +738,7 @@ fn on_check_task(
746 .uri 738 .uri
747 .to_file_path() 739 .to_file_path()
748 .map_err(|()| format!("invalid uri: {}", diag.location.uri))?; 740 .map_err(|()| format!("invalid uri: {}", diag.location.uri))?;
749 let file_id = match world_state.vfs.read().path2file(&path) { 741 let file_id = match global_state.vfs.read().path2file(&path) {
750 Some(file) => FileId(file.0), 742 Some(file) => FileId(file.0),
751 None => { 743 None => {
752 log::error!( 744 log::error!(
@@ -766,7 +758,7 @@ fn on_check_task(
766 } 758 }
767 759
768 CheckTask::Status(status) => { 760 CheckTask::Status(status) => {
769 if world_state.config.client_caps.work_done_progress { 761 if global_state.config.client_caps.work_done_progress {
770 let progress = match status { 762 let progress = match status {
771 Status::Being => { 763 Status::Being => {
772 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { 764 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
@@ -805,7 +797,7 @@ fn on_check_task(
805 Ok(()) 797 Ok(())
806} 798}
807 799
808fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut WorldState) { 800fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut GlobalState) {
809 let subscriptions = state.diagnostics.handle_task(task); 801 let subscriptions = state.diagnostics.handle_task(task);
810 802
811 for file_id in subscriptions { 803 for file_id in subscriptions {
@@ -880,7 +872,7 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
880struct PoolDispatcher<'a> { 872struct PoolDispatcher<'a> {
881 req: Option<Request>, 873 req: Option<Request>,
882 pool: &'a ThreadPool, 874 pool: &'a ThreadPool,
883 world: &'a mut WorldState, 875 global_state: &'a mut GlobalState,
884 pending_requests: &'a mut PendingRequests, 876 pending_requests: &'a mut PendingRequests,
885 msg_sender: &'a Sender<Message>, 877 msg_sender: &'a Sender<Message>,
886 task_sender: &'a Sender<Task>, 878 task_sender: &'a Sender<Task>,
@@ -891,7 +883,7 @@ impl<'a> PoolDispatcher<'a> {
891 /// Dispatches the request onto the current thread 883 /// Dispatches the request onto the current thread
892 fn on_sync<R>( 884 fn on_sync<R>(
893 &mut self, 885 &mut self,
894 f: fn(&mut WorldState, R::Params) -> Result<R::Result>, 886 f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
895 ) -> Result<&mut Self> 887 ) -> Result<&mut Self>
896 where 888 where
897 R: lsp_types::request::Request + 'static, 889 R: lsp_types::request::Request + 'static,
@@ -904,18 +896,21 @@ impl<'a> PoolDispatcher<'a> {
904 return Ok(self); 896 return Ok(self);
905 } 897 }
906 }; 898 };
907 let world = panic::AssertUnwindSafe(&mut *self.world); 899 let world = panic::AssertUnwindSafe(&mut *self.global_state);
908 let task = panic::catch_unwind(move || { 900 let task = panic::catch_unwind(move || {
909 let result = f(world.0, params); 901 let result = f(world.0, params);
910 result_to_task::<R>(id, result) 902 result_to_task::<R>(id, result)
911 }) 903 })
912 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?; 904 .map_err(|_| format!("sync task {:?} panicked", R::METHOD))?;
913 on_task(task, self.msg_sender, self.pending_requests, self.world); 905 on_task(task, self.msg_sender, self.pending_requests, self.global_state);
914 Ok(self) 906 Ok(self)
915 } 907 }
916 908
917 /// Dispatches the request onto thread pool 909 /// Dispatches the request onto thread pool
918 fn on<R>(&mut self, f: fn(WorldSnapshot, R::Params) -> Result<R::Result>) -> Result<&mut Self> 910 fn on<R>(
911 &mut self,
912 f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
913 ) -> Result<&mut Self>
919 where 914 where
920 R: lsp_types::request::Request + 'static, 915 R: lsp_types::request::Request + 'static,
921 R::Params: DeserializeOwned + Send + 'static, 916 R::Params: DeserializeOwned + Send + 'static,
@@ -929,7 +924,7 @@ impl<'a> PoolDispatcher<'a> {
929 }; 924 };
930 925
931 self.pool.execute({ 926 self.pool.execute({
932 let world = self.world.snapshot(); 927 let world = self.global_state.snapshot();
933 let sender = self.task_sender.clone(); 928 let sender = self.task_sender.clone();
934 move || { 929 move || {
935 let result = f(world, params); 930 let result = f(world, params);
@@ -1013,7 +1008,7 @@ where
1013 1008
1014fn update_file_notifications_on_threadpool( 1009fn update_file_notifications_on_threadpool(
1015 pool: &ThreadPool, 1010 pool: &ThreadPool,
1016 world: WorldSnapshot, 1011 world: GlobalStateSnapshot,
1017 task_sender: Sender<Task>, 1012 task_sender: Sender<Task>,
1018 subscriptions: Vec<FileId>, 1013 subscriptions: Vec<FileId>,
1019) { 1014) {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index fab82ff7e..a3361d6dc 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -32,17 +32,16 @@ use crate::{
32 config::RustfmtConfig, 32 config::RustfmtConfig,
33 diagnostics::DiagnosticTask, 33 diagnostics::DiagnosticTask,
34 from_json, from_proto, 34 from_json, from_proto,
35 global_state::GlobalStateSnapshot,
35 lsp_ext::{self, InlayHint, InlayHintsParams}, 36 lsp_ext::{self, InlayHint, InlayHintsParams},
36 to_proto, 37 to_proto, LspError, Result,
37 world::WorldSnapshot,
38 LspError, Result,
39}; 38};
40 39
41pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { 40pub fn handle_analyzer_status(snap: GlobalStateSnapshot, _: ()) -> Result<String> {
42 let _p = profile("handle_analyzer_status"); 41 let _p = profile("handle_analyzer_status");
43 let mut buf = world.status(); 42 let mut buf = snap.status();
44 format_to!(buf, "\n\nrequests:\n"); 43 format_to!(buf, "\n\nrequests:\n");
45 let requests = world.latest_requests.read(); 44 let requests = snap.latest_requests.read();
46 for (is_last, r) in requests.iter() { 45 for (is_last, r) in requests.iter() {
47 let mark = if is_last { "*" } else { " " }; 46 let mark = if is_last { "*" } else { " " };
48 format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis()); 47 format_to!(buf, "{}{:4} {:<36}{}ms\n", mark, r.id, r.method, r.duration.as_millis());
@@ -51,37 +50,37 @@ pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> {
51} 50}
52 51
53pub fn handle_syntax_tree( 52pub fn handle_syntax_tree(
54 world: WorldSnapshot, 53 snap: GlobalStateSnapshot,
55 params: lsp_ext::SyntaxTreeParams, 54 params: lsp_ext::SyntaxTreeParams,
56) -> Result<String> { 55) -> Result<String> {
57 let _p = profile("handle_syntax_tree"); 56 let _p = profile("handle_syntax_tree");
58 let id = from_proto::file_id(&world, &params.text_document.uri)?; 57 let id = from_proto::file_id(&snap, &params.text_document.uri)?;
59 let line_index = world.analysis().file_line_index(id)?; 58 let line_index = snap.analysis().file_line_index(id)?;
60 let text_range = params.range.map(|r| from_proto::text_range(&line_index, r)); 59 let text_range = params.range.map(|r| from_proto::text_range(&line_index, r));
61 let res = world.analysis().syntax_tree(id, text_range)?; 60 let res = snap.analysis().syntax_tree(id, text_range)?;
62 Ok(res) 61 Ok(res)
63} 62}
64 63
65pub fn handle_expand_macro( 64pub fn handle_expand_macro(
66 world: WorldSnapshot, 65 snap: GlobalStateSnapshot,
67 params: lsp_ext::ExpandMacroParams, 66 params: lsp_ext::ExpandMacroParams,
68) -> Result<Option<lsp_ext::ExpandedMacro>> { 67) -> Result<Option<lsp_ext::ExpandedMacro>> {
69 let _p = profile("handle_expand_macro"); 68 let _p = profile("handle_expand_macro");
70 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 69 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
71 let line_index = world.analysis().file_line_index(file_id)?; 70 let line_index = snap.analysis().file_line_index(file_id)?;
72 let offset = from_proto::offset(&line_index, params.position); 71 let offset = from_proto::offset(&line_index, params.position);
73 72
74 let res = world.analysis().expand_macro(FilePosition { file_id, offset })?; 73 let res = snap.analysis().expand_macro(FilePosition { file_id, offset })?;
75 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion })) 74 Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
76} 75}
77 76
78pub fn handle_selection_range( 77pub fn handle_selection_range(
79 world: WorldSnapshot, 78 snap: GlobalStateSnapshot,
80 params: lsp_types::SelectionRangeParams, 79 params: lsp_types::SelectionRangeParams,
81) -> Result<Option<Vec<lsp_types::SelectionRange>>> { 80) -> Result<Option<Vec<lsp_types::SelectionRange>>> {
82 let _p = profile("handle_selection_range"); 81 let _p = profile("handle_selection_range");
83 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 82 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
84 let line_index = world.analysis().file_line_index(file_id)?; 83 let line_index = snap.analysis().file_line_index(file_id)?;
85 let res: Result<Vec<lsp_types::SelectionRange>> = params 84 let res: Result<Vec<lsp_types::SelectionRange>> = params
86 .positions 85 .positions
87 .into_iter() 86 .into_iter()
@@ -93,7 +92,7 @@ pub fn handle_selection_range(
93 loop { 92 loop {
94 ranges.push(range); 93 ranges.push(range);
95 let frange = FileRange { file_id, range }; 94 let frange = FileRange { file_id, range };
96 let next = world.analysis().extend_selection(frange)?; 95 let next = snap.analysis().extend_selection(frange)?;
97 if next == range { 96 if next == range {
98 break; 97 break;
99 } else { 98 } else {
@@ -119,18 +118,18 @@ pub fn handle_selection_range(
119} 118}
120 119
121pub fn handle_matching_brace( 120pub fn handle_matching_brace(
122 world: WorldSnapshot, 121 snap: GlobalStateSnapshot,
123 params: lsp_ext::MatchingBraceParams, 122 params: lsp_ext::MatchingBraceParams,
124) -> Result<Vec<Position>> { 123) -> Result<Vec<Position>> {
125 let _p = profile("handle_matching_brace"); 124 let _p = profile("handle_matching_brace");
126 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 125 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
127 let line_index = world.analysis().file_line_index(file_id)?; 126 let line_index = snap.analysis().file_line_index(file_id)?;
128 let res = params 127 let res = params
129 .positions 128 .positions
130 .into_iter() 129 .into_iter()
131 .map(|position| { 130 .map(|position| {
132 let offset = from_proto::offset(&line_index, position); 131 let offset = from_proto::offset(&line_index, position);
133 let offset = match world.analysis().matching_brace(FilePosition { file_id, offset }) { 132 let offset = match snap.analysis().matching_brace(FilePosition { file_id, offset }) {
134 Ok(Some(matching_brace_offset)) => matching_brace_offset, 133 Ok(Some(matching_brace_offset)) => matching_brace_offset,
135 Err(_) | Ok(None) => offset, 134 Err(_) | Ok(None) => offset,
136 }; 135 };
@@ -141,17 +140,17 @@ pub fn handle_matching_brace(
141} 140}
142 141
143pub fn handle_join_lines( 142pub fn handle_join_lines(
144 world: WorldSnapshot, 143 snap: GlobalStateSnapshot,
145 params: lsp_ext::JoinLinesParams, 144 params: lsp_ext::JoinLinesParams,
146) -> Result<Vec<lsp_types::TextEdit>> { 145) -> Result<Vec<lsp_types::TextEdit>> {
147 let _p = profile("handle_join_lines"); 146 let _p = profile("handle_join_lines");
148 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 147 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
149 let line_index = world.analysis().file_line_index(file_id)?; 148 let line_index = snap.analysis().file_line_index(file_id)?;
150 let line_endings = world.file_line_endings(file_id); 149 let line_endings = snap.file_line_endings(file_id);
151 let mut res = TextEdit::default(); 150 let mut res = TextEdit::default();
152 for range in params.ranges { 151 for range in params.ranges {
153 let range = from_proto::text_range(&line_index, range); 152 let range = from_proto::text_range(&line_index, range);
154 let edit = world.analysis().join_lines(FileRange { file_id, range })?; 153 let edit = snap.analysis().join_lines(FileRange { file_id, range })?;
155 match res.union(edit) { 154 match res.union(edit) {
156 Ok(()) => (), 155 Ok(()) => (),
157 Err(_edit) => { 156 Err(_edit) => {
@@ -164,37 +163,37 @@ pub fn handle_join_lines(
164} 163}
165 164
166pub fn handle_on_enter( 165pub fn handle_on_enter(
167 world: WorldSnapshot, 166 snap: GlobalStateSnapshot,
168 params: lsp_types::TextDocumentPositionParams, 167 params: lsp_types::TextDocumentPositionParams,
169) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> { 168) -> Result<Option<Vec<lsp_ext::SnippetTextEdit>>> {
170 let _p = profile("handle_on_enter"); 169 let _p = profile("handle_on_enter");
171 let position = from_proto::file_position(&world, params)?; 170 let position = from_proto::file_position(&snap, params)?;
172 let edit = match world.analysis().on_enter(position)? { 171 let edit = match snap.analysis().on_enter(position)? {
173 None => return Ok(None), 172 None => return Ok(None),
174 Some(it) => it, 173 Some(it) => it,
175 }; 174 };
176 let line_index = world.analysis().file_line_index(position.file_id)?; 175 let line_index = snap.analysis().file_line_index(position.file_id)?;
177 let line_endings = world.file_line_endings(position.file_id); 176 let line_endings = snap.file_line_endings(position.file_id);
178 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit); 177 let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit);
179 Ok(Some(edit)) 178 Ok(Some(edit))
180} 179}
181 180
182// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`. 181// Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
183pub fn handle_on_type_formatting( 182pub fn handle_on_type_formatting(
184 world: WorldSnapshot, 183 snap: GlobalStateSnapshot,
185 params: lsp_types::DocumentOnTypeFormattingParams, 184 params: lsp_types::DocumentOnTypeFormattingParams,
186) -> Result<Option<Vec<lsp_types::TextEdit>>> { 185) -> Result<Option<Vec<lsp_types::TextEdit>>> {
187 let _p = profile("handle_on_type_formatting"); 186 let _p = profile("handle_on_type_formatting");
188 let mut position = from_proto::file_position(&world, params.text_document_position)?; 187 let mut position = from_proto::file_position(&snap, params.text_document_position)?;
189 let line_index = world.analysis().file_line_index(position.file_id)?; 188 let line_index = snap.analysis().file_line_index(position.file_id)?;
190 let line_endings = world.file_line_endings(position.file_id); 189 let line_endings = snap.file_line_endings(position.file_id);
191 190
192 // in `ra_ide`, the `on_type` invariant is that 191 // in `ra_ide`, the `on_type` invariant is that
193 // `text.char_at(position) == typed_char`. 192 // `text.char_at(position) == typed_char`.
194 position.offset -= TextSize::of('.'); 193 position.offset -= TextSize::of('.');
195 let char_typed = params.ch.chars().next().unwrap_or('\0'); 194 let char_typed = params.ch.chars().next().unwrap_or('\0');
196 assert!({ 195 assert!({
197 let text = world.analysis().file_text(position.file_id)?; 196 let text = snap.analysis().file_text(position.file_id)?;
198 text[usize::from(position.offset)..].starts_with(char_typed) 197 text[usize::from(position.offset)..].starts_with(char_typed)
199 }); 198 });
200 199
@@ -206,7 +205,7 @@ pub fn handle_on_type_formatting(
206 return Ok(None); 205 return Ok(None);
207 } 206 }
208 207
209 let edit = world.analysis().on_char_typed(position, char_typed)?; 208 let edit = snap.analysis().on_char_typed(position, char_typed)?;
210 let mut edit = match edit { 209 let mut edit = match edit {
211 Some(it) => it, 210 Some(it) => it,
212 None => return Ok(None), 211 None => return Ok(None),
@@ -220,16 +219,16 @@ pub fn handle_on_type_formatting(
220} 219}
221 220
222pub fn handle_document_symbol( 221pub fn handle_document_symbol(
223 world: WorldSnapshot, 222 snap: GlobalStateSnapshot,
224 params: lsp_types::DocumentSymbolParams, 223 params: lsp_types::DocumentSymbolParams,
225) -> Result<Option<lsp_types::DocumentSymbolResponse>> { 224) -> Result<Option<lsp_types::DocumentSymbolResponse>> {
226 let _p = profile("handle_document_symbol"); 225 let _p = profile("handle_document_symbol");
227 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 226 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
228 let line_index = world.analysis().file_line_index(file_id)?; 227 let line_index = snap.analysis().file_line_index(file_id)?;
229 228
230 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new(); 229 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
231 230
232 for symbol in world.analysis().file_structure(file_id)? { 231 for symbol in snap.analysis().file_structure(file_id)? {
233 let doc_symbol = DocumentSymbol { 232 let doc_symbol = DocumentSymbol {
234 name: symbol.label, 233 name: symbol.label,
235 detail: symbol.detail, 234 detail: symbol.detail,
@@ -255,10 +254,10 @@ pub fn handle_document_symbol(
255 } 254 }
256 } 255 }
257 256
258 let res = if world.config.client_caps.hierarchical_symbols { 257 let res = if snap.config.client_caps.hierarchical_symbols {
259 document_symbols.into() 258 document_symbols.into()
260 } else { 259 } else {
261 let url = to_proto::url(&world, file_id)?; 260 let url = to_proto::url(&snap, file_id)?;
262 let mut symbol_information = Vec::<SymbolInformation>::new(); 261 let mut symbol_information = Vec::<SymbolInformation>::new();
263 for symbol in document_symbols { 262 for symbol in document_symbols {
264 flatten_document_symbol(&symbol, None, &url, &mut symbol_information); 263 flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
@@ -288,7 +287,7 @@ pub fn handle_document_symbol(
288} 287}
289 288
290pub fn handle_workspace_symbol( 289pub fn handle_workspace_symbol(
291 world: WorldSnapshot, 290 snap: GlobalStateSnapshot,
292 params: lsp_types::WorkspaceSymbolParams, 291 params: lsp_types::WorkspaceSymbolParams,
293) -> Result<Option<Vec<SymbolInformation>>> { 292) -> Result<Option<Vec<SymbolInformation>>> {
294 let _p = profile("handle_workspace_symbol"); 293 let _p = profile("handle_workspace_symbol");
@@ -306,22 +305,22 @@ pub fn handle_workspace_symbol(
306 q.limit(128); 305 q.limit(128);
307 q 306 q
308 }; 307 };
309 let mut res = exec_query(&world, query)?; 308 let mut res = exec_query(&snap, query)?;
310 if res.is_empty() && !all_symbols { 309 if res.is_empty() && !all_symbols {
311 let mut query = Query::new(params.query); 310 let mut query = Query::new(params.query);
312 query.limit(128); 311 query.limit(128);
313 res = exec_query(&world, query)?; 312 res = exec_query(&snap, query)?;
314 } 313 }
315 314
316 return Ok(Some(res)); 315 return Ok(Some(res));
317 316
318 fn exec_query(world: &WorldSnapshot, query: Query) -> Result<Vec<SymbolInformation>> { 317 fn exec_query(snap: &GlobalStateSnapshot, query: Query) -> Result<Vec<SymbolInformation>> {
319 let mut res = Vec::new(); 318 let mut res = Vec::new();
320 for nav in world.analysis().symbol_search(query)? { 319 for nav in snap.analysis().symbol_search(query)? {
321 let info = SymbolInformation { 320 let info = SymbolInformation {
322 name: nav.name().to_string(), 321 name: nav.name().to_string(),
323 kind: to_proto::symbol_kind(nav.kind()), 322 kind: to_proto::symbol_kind(nav.kind()),
324 location: to_proto::location(world, nav.file_range())?, 323 location: to_proto::location(snap, nav.file_range())?,
325 container_name: nav.container_name().map(|v| v.to_string()), 324 container_name: nav.container_name().map(|v| v.to_string()),
326 deprecated: None, 325 deprecated: None,
327 }; 326 };
@@ -332,73 +331,73 @@ pub fn handle_workspace_symbol(
332} 331}
333 332
334pub fn handle_goto_definition( 333pub fn handle_goto_definition(
335 world: WorldSnapshot, 334 snap: GlobalStateSnapshot,
336 params: lsp_types::GotoDefinitionParams, 335 params: lsp_types::GotoDefinitionParams,
337) -> Result<Option<lsp_types::GotoDefinitionResponse>> { 336) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
338 let _p = profile("handle_goto_definition"); 337 let _p = profile("handle_goto_definition");
339 let position = from_proto::file_position(&world, params.text_document_position_params)?; 338 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
340 let nav_info = match world.analysis().goto_definition(position)? { 339 let nav_info = match snap.analysis().goto_definition(position)? {
341 None => return Ok(None), 340 None => return Ok(None),
342 Some(it) => it, 341 Some(it) => it,
343 }; 342 };
344 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 343 let src = FileRange { file_id: position.file_id, range: nav_info.range };
345 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 344 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
346 Ok(Some(res)) 345 Ok(Some(res))
347} 346}
348 347
349pub fn handle_goto_implementation( 348pub fn handle_goto_implementation(
350 world: WorldSnapshot, 349 snap: GlobalStateSnapshot,
351 params: lsp_types::request::GotoImplementationParams, 350 params: lsp_types::request::GotoImplementationParams,
352) -> Result<Option<lsp_types::request::GotoImplementationResponse>> { 351) -> Result<Option<lsp_types::request::GotoImplementationResponse>> {
353 let _p = profile("handle_goto_implementation"); 352 let _p = profile("handle_goto_implementation");
354 let position = from_proto::file_position(&world, params.text_document_position_params)?; 353 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
355 let nav_info = match world.analysis().goto_implementation(position)? { 354 let nav_info = match snap.analysis().goto_implementation(position)? {
356 None => return Ok(None), 355 None => return Ok(None),
357 Some(it) => it, 356 Some(it) => it,
358 }; 357 };
359 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 358 let src = FileRange { file_id: position.file_id, range: nav_info.range };
360 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 359 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
361 Ok(Some(res)) 360 Ok(Some(res))
362} 361}
363 362
364pub fn handle_goto_type_definition( 363pub fn handle_goto_type_definition(
365 world: WorldSnapshot, 364 snap: GlobalStateSnapshot,
366 params: lsp_types::request::GotoTypeDefinitionParams, 365 params: lsp_types::request::GotoTypeDefinitionParams,
367) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> { 366) -> Result<Option<lsp_types::request::GotoTypeDefinitionResponse>> {
368 let _p = profile("handle_goto_type_definition"); 367 let _p = profile("handle_goto_type_definition");
369 let position = from_proto::file_position(&world, params.text_document_position_params)?; 368 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
370 let nav_info = match world.analysis().goto_type_definition(position)? { 369 let nav_info = match snap.analysis().goto_type_definition(position)? {
371 None => return Ok(None), 370 None => return Ok(None),
372 Some(it) => it, 371 Some(it) => it,
373 }; 372 };
374 let src = FileRange { file_id: position.file_id, range: nav_info.range }; 373 let src = FileRange { file_id: position.file_id, range: nav_info.range };
375 let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?; 374 let res = to_proto::goto_definition_response(&snap, Some(src), nav_info.info)?;
376 Ok(Some(res)) 375 Ok(Some(res))
377} 376}
378 377
379pub fn handle_parent_module( 378pub fn handle_parent_module(
380 world: WorldSnapshot, 379 snap: GlobalStateSnapshot,
381 params: lsp_types::TextDocumentPositionParams, 380 params: lsp_types::TextDocumentPositionParams,
382) -> Result<Option<lsp_types::GotoDefinitionResponse>> { 381) -> Result<Option<lsp_types::GotoDefinitionResponse>> {
383 let _p = profile("handle_parent_module"); 382 let _p = profile("handle_parent_module");
384 let position = from_proto::file_position(&world, params)?; 383 let position = from_proto::file_position(&snap, params)?;
385 let navs = world.analysis().parent_module(position)?; 384 let navs = snap.analysis().parent_module(position)?;
386 let res = to_proto::goto_definition_response(&world, None, navs)?; 385 let res = to_proto::goto_definition_response(&snap, None, navs)?;
387 Ok(Some(res)) 386 Ok(Some(res))
388} 387}
389 388
390pub fn handle_runnables( 389pub fn handle_runnables(
391 world: WorldSnapshot, 390 snap: GlobalStateSnapshot,
392 params: lsp_ext::RunnablesParams, 391 params: lsp_ext::RunnablesParams,
393) -> Result<Vec<lsp_ext::Runnable>> { 392) -> Result<Vec<lsp_ext::Runnable>> {
394 let _p = profile("handle_runnables"); 393 let _p = profile("handle_runnables");
395 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 394 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
396 let line_index = world.analysis().file_line_index(file_id)?; 395 let line_index = snap.analysis().file_line_index(file_id)?;
397 let offset = params.position.map(|it| from_proto::offset(&line_index, it)); 396 let offset = params.position.map(|it| from_proto::offset(&line_index, it));
398 let mut res = Vec::new(); 397 let mut res = Vec::new();
399 let workspace_root = world.workspace_root_for(file_id); 398 let workspace_root = snap.workspace_root_for(file_id);
400 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; 399 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
401 for runnable in world.analysis().runnables(file_id)? { 400 for runnable in snap.analysis().runnables(file_id)? {
402 if let Some(offset) = offset { 401 if let Some(offset) = offset {
403 if !runnable.nav.full_range().contains_inclusive(offset) { 402 if !runnable.nav.full_range().contains_inclusive(offset) {
404 continue; 403 continue;
@@ -413,7 +412,7 @@ pub fn handle_runnables(
413 } 412 }
414 } 413 }
415 } 414 }
416 res.push(to_proto::runnable(&world, file_id, runnable)?); 415 res.push(to_proto::runnable(&snap, file_id, runnable)?);
417 } 416 }
418 417
419 // Add `cargo check` and `cargo test` for the whole package 418 // Add `cargo check` and `cargo test` for the whole package
@@ -453,16 +452,16 @@ pub fn handle_runnables(
453} 452}
454 453
455pub fn handle_completion( 454pub fn handle_completion(
456 world: WorldSnapshot, 455 snap: GlobalStateSnapshot,
457 params: lsp_types::CompletionParams, 456 params: lsp_types::CompletionParams,
458) -> Result<Option<lsp_types::CompletionResponse>> { 457) -> Result<Option<lsp_types::CompletionResponse>> {
459 let _p = profile("handle_completion"); 458 let _p = profile("handle_completion");
460 let position = from_proto::file_position(&world, params.text_document_position)?; 459 let position = from_proto::file_position(&snap, params.text_document_position)?;
461 let completion_triggered_after_single_colon = { 460 let completion_triggered_after_single_colon = {
462 let mut res = false; 461 let mut res = false;
463 if let Some(ctx) = params.context { 462 if let Some(ctx) = params.context {
464 if ctx.trigger_character.unwrap_or_default() == ":" { 463 if ctx.trigger_character.unwrap_or_default() == ":" {
465 let source_file = world.analysis().parse(position.file_id)?; 464 let source_file = snap.analysis().parse(position.file_id)?;
466 let syntax = source_file.syntax(); 465 let syntax = source_file.syntax();
467 let text = syntax.text(); 466 let text = syntax.text();
468 if let Some(next_char) = text.char_at(position.offset) { 467 if let Some(next_char) = text.char_at(position.offset) {
@@ -480,12 +479,12 @@ pub fn handle_completion(
480 return Ok(None); 479 return Ok(None);
481 } 480 }
482 481
483 let items = match world.analysis().completions(&world.config.completion, position)? { 482 let items = match snap.analysis().completions(&snap.config.completion, position)? {
484 None => return Ok(None), 483 None => return Ok(None),
485 Some(items) => items, 484 Some(items) => items,
486 }; 485 };
487 let line_index = world.analysis().file_line_index(position.file_id)?; 486 let line_index = snap.analysis().file_line_index(position.file_id)?;
488 let line_endings = world.file_line_endings(position.file_id); 487 let line_endings = snap.file_line_endings(position.file_id);
489 let items: Vec<CompletionItem> = items 488 let items: Vec<CompletionItem> = items
490 .into_iter() 489 .into_iter()
491 .map(|item| to_proto::completion_item(&line_index, line_endings, item)) 490 .map(|item| to_proto::completion_item(&line_index, line_endings, item))
@@ -495,15 +494,15 @@ pub fn handle_completion(
495} 494}
496 495
497pub fn handle_folding_range( 496pub fn handle_folding_range(
498 world: WorldSnapshot, 497 snap: GlobalStateSnapshot,
499 params: FoldingRangeParams, 498 params: FoldingRangeParams,
500) -> Result<Option<Vec<FoldingRange>>> { 499) -> Result<Option<Vec<FoldingRange>>> {
501 let _p = profile("handle_folding_range"); 500 let _p = profile("handle_folding_range");
502 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 501 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
503 let folds = world.analysis().folding_ranges(file_id)?; 502 let folds = snap.analysis().folding_ranges(file_id)?;
504 let text = world.analysis().file_text(file_id)?; 503 let text = snap.analysis().file_text(file_id)?;
505 let line_index = world.analysis().file_line_index(file_id)?; 504 let line_index = snap.analysis().file_line_index(file_id)?;
506 let line_folding_only = world.config.client_caps.line_folding_only; 505 let line_folding_only = snap.config.client_caps.line_folding_only;
507 let res = folds 506 let res = folds
508 .into_iter() 507 .into_iter()
509 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) 508 .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it))
@@ -512,16 +511,16 @@ pub fn handle_folding_range(
512} 511}
513 512
514pub fn handle_signature_help( 513pub fn handle_signature_help(
515 world: WorldSnapshot, 514 snap: GlobalStateSnapshot,
516 params: lsp_types::SignatureHelpParams, 515 params: lsp_types::SignatureHelpParams,
517) -> Result<Option<lsp_types::SignatureHelp>> { 516) -> Result<Option<lsp_types::SignatureHelp>> {
518 let _p = profile("handle_signature_help"); 517 let _p = profile("handle_signature_help");
519 let position = from_proto::file_position(&world, params.text_document_position_params)?; 518 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
520 let call_info = match world.analysis().call_info(position)? { 519 let call_info = match snap.analysis().call_info(position)? {
521 None => return Ok(None), 520 None => return Ok(None),
522 Some(it) => it, 521 Some(it) => it,
523 }; 522 };
524 let concise = !world.config.call_info_full; 523 let concise = !snap.config.call_info_full;
525 let mut active_parameter = call_info.active_parameter.map(|it| it as i64); 524 let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
526 if concise && call_info.signature.has_self_param { 525 if concise && call_info.signature.has_self_param {
527 active_parameter = active_parameter.map(|it| it.saturating_sub(1)); 526 active_parameter = active_parameter.map(|it| it.saturating_sub(1));
@@ -535,14 +534,17 @@ pub fn handle_signature_help(
535 })) 534 }))
536} 535}
537 536
538pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result<Option<Hover>> { 537pub fn handle_hover(
538 snap: GlobalStateSnapshot,
539 params: lsp_types::HoverParams,
540) -> Result<Option<Hover>> {
539 let _p = profile("handle_hover"); 541 let _p = profile("handle_hover");
540 let position = from_proto::file_position(&world, params.text_document_position_params)?; 542 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
541 let info = match world.analysis().hover(position)? { 543 let info = match snap.analysis().hover(position)? {
542 None => return Ok(None), 544 None => return Ok(None),
543 Some(info) => info, 545 Some(info) => info,
544 }; 546 };
545 let line_index = world.analysis.file_line_index(position.file_id)?; 547 let line_index = snap.analysis.file_line_index(position.file_id)?;
546 let range = to_proto::range(&line_index, info.range); 548 let range = to_proto::range(&line_index, info.range);
547 let res = Hover { 549 let res = Hover {
548 contents: HoverContents::Markup(MarkupContent { 550 contents: HoverContents::Markup(MarkupContent {
@@ -555,26 +557,29 @@ pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Res
555} 557}
556 558
557pub fn handle_prepare_rename( 559pub fn handle_prepare_rename(
558 world: WorldSnapshot, 560 snap: GlobalStateSnapshot,
559 params: lsp_types::TextDocumentPositionParams, 561 params: lsp_types::TextDocumentPositionParams,
560) -> Result<Option<PrepareRenameResponse>> { 562) -> Result<Option<PrepareRenameResponse>> {
561 let _p = profile("handle_prepare_rename"); 563 let _p = profile("handle_prepare_rename");
562 let position = from_proto::file_position(&world, params)?; 564 let position = from_proto::file_position(&snap, params)?;
563 565
564 let optional_change = world.analysis().rename(position, "dummy")?; 566 let optional_change = snap.analysis().rename(position, "dummy")?;
565 let range = match optional_change { 567 let range = match optional_change {
566 None => return Ok(None), 568 None => return Ok(None),
567 Some(it) => it.range, 569 Some(it) => it.range,
568 }; 570 };
569 571
570 let line_index = world.analysis().file_line_index(position.file_id)?; 572 let line_index = snap.analysis().file_line_index(position.file_id)?;
571 let range = to_proto::range(&line_index, range); 573 let range = to_proto::range(&line_index, range);
572 Ok(Some(PrepareRenameResponse::Range(range))) 574 Ok(Some(PrepareRenameResponse::Range(range)))
573} 575}
574 576
575pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { 577pub fn handle_rename(
578 snap: GlobalStateSnapshot,
579 params: RenameParams,
580) -> Result<Option<WorkspaceEdit>> {
576 let _p = profile("handle_rename"); 581 let _p = profile("handle_rename");
577 let position = from_proto::file_position(&world, params.text_document_position)?; 582 let position = from_proto::file_position(&snap, params.text_document_position)?;
578 583
579 if params.new_name.is_empty() { 584 if params.new_name.is_empty() {
580 return Err(LspError::new( 585 return Err(LspError::new(
@@ -584,36 +589,36 @@ pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Optio
584 .into()); 589 .into());
585 } 590 }
586 591
587 let optional_change = world.analysis().rename(position, &*params.new_name)?; 592 let optional_change = snap.analysis().rename(position, &*params.new_name)?;
588 let source_change = match optional_change { 593 let source_change = match optional_change {
589 None => return Ok(None), 594 None => return Ok(None),
590 Some(it) => it.info, 595 Some(it) => it.info,
591 }; 596 };
592 let workspace_edit = to_proto::workspace_edit(&world, source_change)?; 597 let workspace_edit = to_proto::workspace_edit(&snap, source_change)?;
593 Ok(Some(workspace_edit)) 598 Ok(Some(workspace_edit))
594} 599}
595 600
596pub fn handle_references( 601pub fn handle_references(
597 world: WorldSnapshot, 602 snap: GlobalStateSnapshot,
598 params: lsp_types::ReferenceParams, 603 params: lsp_types::ReferenceParams,
599) -> Result<Option<Vec<Location>>> { 604) -> Result<Option<Vec<Location>>> {
600 let _p = profile("handle_references"); 605 let _p = profile("handle_references");
601 let position = from_proto::file_position(&world, params.text_document_position)?; 606 let position = from_proto::file_position(&snap, params.text_document_position)?;
602 607
603 let refs = match world.analysis().find_all_refs(position, None)? { 608 let refs = match snap.analysis().find_all_refs(position, None)? {
604 None => return Ok(None), 609 None => return Ok(None),
605 Some(refs) => refs, 610 Some(refs) => refs,
606 }; 611 };
607 612
608 let locations = if params.context.include_declaration { 613 let locations = if params.context.include_declaration {
609 refs.into_iter() 614 refs.into_iter()
610 .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) 615 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
611 .collect() 616 .collect()
612 } else { 617 } else {
613 // Only iterate over the references if include_declaration was false 618 // Only iterate over the references if include_declaration was false
614 refs.references() 619 refs.references()
615 .iter() 620 .iter()
616 .filter_map(|reference| to_proto::location(&world, reference.file_range).ok()) 621 .filter_map(|reference| to_proto::location(&snap, reference.file_range).ok())
617 .collect() 622 .collect()
618 }; 623 };
619 624
@@ -621,24 +626,24 @@ pub fn handle_references(
621} 626}
622 627
623pub fn handle_formatting( 628pub fn handle_formatting(
624 world: WorldSnapshot, 629 snap: GlobalStateSnapshot,
625 params: DocumentFormattingParams, 630 params: DocumentFormattingParams,
626) -> Result<Option<Vec<lsp_types::TextEdit>>> { 631) -> Result<Option<Vec<lsp_types::TextEdit>>> {
627 let _p = profile("handle_formatting"); 632 let _p = profile("handle_formatting");
628 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 633 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
629 let file = world.analysis().file_text(file_id)?; 634 let file = snap.analysis().file_text(file_id)?;
630 let crate_ids = world.analysis().crate_for(file_id)?; 635 let crate_ids = snap.analysis().crate_for(file_id)?;
631 636
632 let file_line_index = world.analysis().file_line_index(file_id)?; 637 let file_line_index = snap.analysis().file_line_index(file_id)?;
633 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); 638 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str()));
634 639
635 let mut rustfmt = match &world.config.rustfmt { 640 let mut rustfmt = match &snap.config.rustfmt {
636 RustfmtConfig::Rustfmt { extra_args } => { 641 RustfmtConfig::Rustfmt { extra_args } => {
637 let mut cmd = process::Command::new("rustfmt"); 642 let mut cmd = process::Command::new("rustfmt");
638 cmd.args(extra_args); 643 cmd.args(extra_args);
639 if let Some(&crate_id) = crate_ids.first() { 644 if let Some(&crate_id) = crate_ids.first() {
640 // Assume all crates are in the same edition 645 // Assume all crates are in the same edition
641 let edition = world.analysis().crate_edition(crate_id)?; 646 let edition = snap.analysis().crate_edition(crate_id)?;
642 cmd.arg("--edition"); 647 cmd.arg("--edition");
643 cmd.arg(edition.to_string()); 648 cmd.arg(edition.to_string());
644 } 649 }
@@ -697,15 +702,14 @@ pub fn handle_formatting(
697} 702}
698 703
699fn handle_fixes( 704fn handle_fixes(
700 world: &WorldSnapshot, 705 snap: &GlobalStateSnapshot,
701 params: &lsp_types::CodeActionParams, 706 params: &lsp_types::CodeActionParams,
702 res: &mut Vec<lsp_ext::CodeAction>, 707 res: &mut Vec<lsp_ext::CodeAction>,
703) -> Result<()> { 708) -> Result<()> {
704 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 709 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
705 let line_index = world.analysis().file_line_index(file_id)?; 710 let line_index = snap.analysis().file_line_index(file_id)?;
706 let range = from_proto::text_range(&line_index, params.range); 711 let range = from_proto::text_range(&line_index, params.range);
707 712 let diagnostics = snap.analysis().diagnostics(file_id)?;
708 let diagnostics = world.analysis().diagnostics(file_id)?;
709 713
710 let fixes_from_diagnostics = diagnostics 714 let fixes_from_diagnostics = diagnostics
711 .into_iter() 715 .into_iter()
@@ -714,18 +718,19 @@ fn handle_fixes(
714 .map(|(_range, fix)| fix); 718 .map(|(_range, fix)| fix);
715 for fix in fixes_from_diagnostics { 719 for fix in fixes_from_diagnostics {
716 let title = fix.label; 720 let title = fix.label;
717 let edit = to_proto::snippet_workspace_edit(&world, fix.source_change)?; 721 let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?;
718 let action = lsp_ext::CodeAction { 722 let action = lsp_ext::CodeAction {
719 title, 723 title,
720 id: None, 724 id: None,
721 group: None, 725 group: None,
722 kind: None, 726 kind: Some(lsp_types::code_action_kind::QUICKFIX.into()),
723 edit: Some(edit), 727 edit: Some(edit),
724 command: None, 728 command: None,
725 }; 729 };
726 res.push(action); 730 res.push(action);
727 } 731 }
728 for fix in world.check_fixes.get(&file_id).into_iter().flatten() { 732
733 for fix in snap.check_fixes.get(&file_id).into_iter().flatten() {
729 let fix_range = from_proto::text_range(&line_index, fix.range); 734 let fix_range = from_proto::text_range(&line_index, fix.range);
730 if fix_range.intersect(range).is_none() { 735 if fix_range.intersect(range).is_none() {
731 continue; 736 continue;
@@ -736,37 +741,34 @@ fn handle_fixes(
736} 741}
737 742
738pub fn handle_code_action( 743pub fn handle_code_action(
739 world: WorldSnapshot, 744 snap: GlobalStateSnapshot,
740 params: lsp_types::CodeActionParams, 745 params: lsp_types::CodeActionParams,
741) -> Result<Option<Vec<lsp_ext::CodeAction>>> { 746) -> Result<Option<Vec<lsp_ext::CodeAction>>> {
742 let _p = profile("handle_code_action"); 747 let _p = profile("handle_code_action");
743 // We intentionally don't support command-based actions, as those either 748 // We intentionally don't support command-based actions, as those either
744 // requires custom client-code anyway, or requires server-initiated edits. 749 // requires custom client-code anyway, or requires server-initiated edits.
745 // Server initiated edits break causality, so we avoid those as well. 750 // Server initiated edits break causality, so we avoid those as well.
746 if !world.config.client_caps.code_action_literals { 751 if !snap.config.client_caps.code_action_literals {
747 return Ok(None); 752 return Ok(None);
748 } 753 }
749 754
750 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 755 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
751 let line_index = world.analysis().file_line_index(file_id)?; 756 let line_index = snap.analysis().file_line_index(file_id)?;
752 let range = from_proto::text_range(&line_index, params.range); 757 let range = from_proto::text_range(&line_index, params.range);
753 let frange = FileRange { file_id, range }; 758 let frange = FileRange { file_id, range };
754 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 759 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
755 760
756 handle_fixes(&world, &params, &mut res)?; 761 handle_fixes(&snap, &params, &mut res)?;
757 762
758 if world.config.client_caps.resolve_code_action { 763 if snap.config.client_caps.resolve_code_action {
759 for (index, assist) in world 764 for (index, assist) in
760 .analysis() 765 snap.analysis().unresolved_assists(&snap.config.assist, frange)?.into_iter().enumerate()
761 .unresolved_assists(&world.config.assist, frange)?
762 .into_iter()
763 .enumerate()
764 { 766 {
765 res.push(to_proto::unresolved_code_action(&world, assist, index)?); 767 res.push(to_proto::unresolved_code_action(&snap, assist, index)?);
766 } 768 }
767 } else { 769 } else {
768 for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() { 770 for assist in snap.analysis().resolved_assists(&snap.config.assist, frange)?.into_iter() {
769 res.push(to_proto::resolved_code_action(&world, assist)?); 771 res.push(to_proto::resolved_code_action(&snap, assist)?);
770 } 772 }
771 } 773 }
772 774
@@ -774,43 +776,43 @@ pub fn handle_code_action(
774} 776}
775 777
776pub fn handle_resolve_code_action( 778pub fn handle_resolve_code_action(
777 world: WorldSnapshot, 779 snap: GlobalStateSnapshot,
778 params: lsp_ext::ResolveCodeActionParams, 780 params: lsp_ext::ResolveCodeActionParams,
779) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> { 781) -> Result<Option<lsp_ext::SnippetWorkspaceEdit>> {
780 let _p = profile("handle_resolve_code_action"); 782 let _p = profile("handle_resolve_code_action");
781 let file_id = from_proto::file_id(&world, &params.code_action_params.text_document.uri)?; 783 let file_id = from_proto::file_id(&snap, &params.code_action_params.text_document.uri)?;
782 let line_index = world.analysis().file_line_index(file_id)?; 784 let line_index = snap.analysis().file_line_index(file_id)?;
783 let range = from_proto::text_range(&line_index, params.code_action_params.range); 785 let range = from_proto::text_range(&line_index, params.code_action_params.range);
784 let frange = FileRange { file_id, range }; 786 let frange = FileRange { file_id, range };
785 787
786 let assists = world.analysis().resolved_assists(&world.config.assist, frange)?; 788 let assists = snap.analysis().resolved_assists(&snap.config.assist, frange)?;
787 let id_components = params.id.split(":").collect::<Vec<&str>>(); 789 let id_components = params.id.split(":").collect::<Vec<&str>>();
788 let index = id_components.last().unwrap().parse::<usize>().unwrap(); 790 let index = id_components.last().unwrap().parse::<usize>().unwrap();
789 let id_string = id_components.first().unwrap(); 791 let id_string = id_components.first().unwrap();
790 let assist = &assists[index]; 792 let assist = &assists[index];
791 assert!(assist.assist.id.0 == *id_string); 793 assert!(assist.assist.id.0 == *id_string);
792 Ok(to_proto::resolved_code_action(&world, assist.clone())?.edit) 794 Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit)
793} 795}
794 796
795pub fn handle_code_lens( 797pub fn handle_code_lens(
796 world: WorldSnapshot, 798 snap: GlobalStateSnapshot,
797 params: lsp_types::CodeLensParams, 799 params: lsp_types::CodeLensParams,
798) -> Result<Option<Vec<CodeLens>>> { 800) -> Result<Option<Vec<CodeLens>>> {
799 let _p = profile("handle_code_lens"); 801 let _p = profile("handle_code_lens");
800 let mut lenses: Vec<CodeLens> = Default::default(); 802 let mut lenses: Vec<CodeLens> = Default::default();
801 803
802 if world.config.lens.none() { 804 if snap.config.lens.none() {
803 // early return before any db query! 805 // early return before any db query!
804 return Ok(Some(lenses)); 806 return Ok(Some(lenses));
805 } 807 }
806 808
807 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 809 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
808 let line_index = world.analysis().file_line_index(file_id)?; 810 let line_index = snap.analysis().file_line_index(file_id)?;
809 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; 811 let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?;
810 812
811 if world.config.lens.runnable() { 813 if snap.config.lens.runnable() {
812 // Gather runnables 814 // Gather runnables
813 for runnable in world.analysis().runnables(file_id)? { 815 for runnable in snap.analysis().runnables(file_id)? {
814 let (run_title, debugee) = match &runnable.kind { 816 let (run_title, debugee) = match &runnable.kind {
815 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => { 817 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => {
816 ("â–¶\u{fe0e} Run Test", true) 818 ("â–¶\u{fe0e} Run Test", true)
@@ -836,8 +838,8 @@ pub fn handle_code_lens(
836 }; 838 };
837 839
838 let range = to_proto::range(&line_index, runnable.nav.range()); 840 let range = to_proto::range(&line_index, runnable.nav.range());
839 let r = to_proto::runnable(&world, file_id, runnable)?; 841 let r = to_proto::runnable(&snap, file_id, runnable)?;
840 if world.config.lens.run { 842 if snap.config.lens.run {
841 let lens = CodeLens { 843 let lens = CodeLens {
842 range, 844 range,
843 command: Some(Command { 845 command: Some(Command {
@@ -850,7 +852,7 @@ pub fn handle_code_lens(
850 lenses.push(lens); 852 lenses.push(lens);
851 } 853 }
852 854
853 if debugee && world.config.lens.debug { 855 if debugee && snap.config.lens.debug {
854 let debug_lens = CodeLens { 856 let debug_lens = CodeLens {
855 range, 857 range,
856 command: Some(Command { 858 command: Some(Command {
@@ -865,11 +867,10 @@ pub fn handle_code_lens(
865 } 867 }
866 } 868 }
867 869
868 if world.config.lens.impementations { 870 if snap.config.lens.impementations {
869 // Handle impls 871 // Handle impls
870 lenses.extend( 872 lenses.extend(
871 world 873 snap.analysis()
872 .analysis()
873 .file_structure(file_id)? 874 .file_structure(file_id)?
874 .into_iter() 875 .into_iter()
875 .filter(|it| match it.kind { 876 .filter(|it| match it.kind {
@@ -904,14 +905,17 @@ enum CodeLensResolveData {
904 Impls(lsp_types::request::GotoImplementationParams), 905 Impls(lsp_types::request::GotoImplementationParams),
905} 906}
906 907
907pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { 908pub fn handle_code_lens_resolve(
909 snap: GlobalStateSnapshot,
910 code_lens: CodeLens,
911) -> Result<CodeLens> {
908 let _p = profile("handle_code_lens_resolve"); 912 let _p = profile("handle_code_lens_resolve");
909 let data = code_lens.data.unwrap(); 913 let data = code_lens.data.unwrap();
910 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?; 914 let resolve = from_json::<Option<CodeLensResolveData>>("CodeLensResolveData", data)?;
911 match resolve { 915 match resolve {
912 Some(CodeLensResolveData::Impls(lens_params)) => { 916 Some(CodeLensResolveData::Impls(lens_params)) => {
913 let locations: Vec<Location> = 917 let locations: Vec<Location> =
914 match handle_goto_implementation(world, lens_params.clone())? { 918 match handle_goto_implementation(snap, lens_params.clone())? {
915 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc], 919 Some(lsp_types::GotoDefinitionResponse::Scalar(loc)) => vec![loc],
916 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs, 920 Some(lsp_types::GotoDefinitionResponse::Array(locs)) => locs,
917 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links 921 Some(lsp_types::GotoDefinitionResponse::Link(links)) => links
@@ -950,14 +954,14 @@ pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Re
950} 954}
951 955
952pub fn handle_document_highlight( 956pub fn handle_document_highlight(
953 world: WorldSnapshot, 957 snap: GlobalStateSnapshot,
954 params: lsp_types::DocumentHighlightParams, 958 params: lsp_types::DocumentHighlightParams,
955) -> Result<Option<Vec<DocumentHighlight>>> { 959) -> Result<Option<Vec<DocumentHighlight>>> {
956 let _p = profile("handle_document_highlight"); 960 let _p = profile("handle_document_highlight");
957 let position = from_proto::file_position(&world, params.text_document_position_params)?; 961 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
958 let line_index = world.analysis().file_line_index(position.file_id)?; 962 let line_index = snap.analysis().file_line_index(position.file_id)?;
959 963
960 let refs = match world 964 let refs = match snap
961 .analysis() 965 .analysis()
962 .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))? 966 .find_all_refs(position, Some(SearchScope::single_file(position.file_id)))?
963 { 967 {
@@ -977,19 +981,19 @@ pub fn handle_document_highlight(
977} 981}
978 982
979pub fn handle_ssr( 983pub fn handle_ssr(
980 world: WorldSnapshot, 984 snap: GlobalStateSnapshot,
981 params: lsp_ext::SsrParams, 985 params: lsp_ext::SsrParams,
982) -> Result<lsp_types::WorkspaceEdit> { 986) -> Result<lsp_types::WorkspaceEdit> {
983 let _p = profile("handle_ssr"); 987 let _p = profile("handle_ssr");
984 let source_change = 988 let source_change =
985 world.analysis().structural_search_replace(&params.query, params.parse_only)??; 989 snap.analysis().structural_search_replace(&params.query, params.parse_only)??;
986 to_proto::workspace_edit(&world, source_change) 990 to_proto::workspace_edit(&snap, source_change)
987} 991}
988 992
989pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { 993pub fn publish_diagnostics(snap: &GlobalStateSnapshot, file_id: FileId) -> Result<DiagnosticTask> {
990 let _p = profile("publish_diagnostics"); 994 let _p = profile("publish_diagnostics");
991 let line_index = world.analysis().file_line_index(file_id)?; 995 let line_index = snap.analysis().file_line_index(file_id)?;
992 let diagnostics: Vec<Diagnostic> = world 996 let diagnostics: Vec<Diagnostic> = snap
993 .analysis() 997 .analysis()
994 .diagnostics(file_id)? 998 .diagnostics(file_id)?
995 .into_iter() 999 .into_iter()
@@ -1007,28 +1011,28 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
1007} 1011}
1008 1012
1009pub fn handle_inlay_hints( 1013pub fn handle_inlay_hints(
1010 world: WorldSnapshot, 1014 snap: GlobalStateSnapshot,
1011 params: InlayHintsParams, 1015 params: InlayHintsParams,
1012) -> Result<Vec<InlayHint>> { 1016) -> Result<Vec<InlayHint>> {
1013 let _p = profile("handle_inlay_hints"); 1017 let _p = profile("handle_inlay_hints");
1014 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 1018 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1015 let analysis = world.analysis(); 1019 let analysis = snap.analysis();
1016 let line_index = analysis.file_line_index(file_id)?; 1020 let line_index = analysis.file_line_index(file_id)?;
1017 Ok(analysis 1021 Ok(analysis
1018 .inlay_hints(file_id, &world.config.inlay_hints)? 1022 .inlay_hints(file_id, &snap.config.inlay_hints)?
1019 .into_iter() 1023 .into_iter()
1020 .map(|it| to_proto::inlay_int(&line_index, it)) 1024 .map(|it| to_proto::inlay_int(&line_index, it))
1021 .collect()) 1025 .collect())
1022} 1026}
1023 1027
1024pub fn handle_call_hierarchy_prepare( 1028pub fn handle_call_hierarchy_prepare(
1025 world: WorldSnapshot, 1029 snap: GlobalStateSnapshot,
1026 params: CallHierarchyPrepareParams, 1030 params: CallHierarchyPrepareParams,
1027) -> Result<Option<Vec<CallHierarchyItem>>> { 1031) -> Result<Option<Vec<CallHierarchyItem>>> {
1028 let _p = profile("handle_call_hierarchy_prepare"); 1032 let _p = profile("handle_call_hierarchy_prepare");
1029 let position = from_proto::file_position(&world, params.text_document_position_params)?; 1033 let position = from_proto::file_position(&snap, params.text_document_position_params)?;
1030 1034
1031 let nav_info = match world.analysis().call_hierarchy(position)? { 1035 let nav_info = match snap.analysis().call_hierarchy(position)? {
1032 None => return Ok(None), 1036 None => return Ok(None),
1033 Some(it) => it, 1037 Some(it) => it,
1034 }; 1038 };
@@ -1037,24 +1041,24 @@ pub fn handle_call_hierarchy_prepare(
1037 let res = navs 1041 let res = navs
1038 .into_iter() 1042 .into_iter()
1039 .filter(|it| it.kind() == SyntaxKind::FN_DEF) 1043 .filter(|it| it.kind() == SyntaxKind::FN_DEF)
1040 .map(|it| to_proto::call_hierarchy_item(&world, it)) 1044 .map(|it| to_proto::call_hierarchy_item(&snap, it))
1041 .collect::<Result<Vec<_>>>()?; 1045 .collect::<Result<Vec<_>>>()?;
1042 1046
1043 Ok(Some(res)) 1047 Ok(Some(res))
1044} 1048}
1045 1049
1046pub fn handle_call_hierarchy_incoming( 1050pub fn handle_call_hierarchy_incoming(
1047 world: WorldSnapshot, 1051 snap: GlobalStateSnapshot,
1048 params: CallHierarchyIncomingCallsParams, 1052 params: CallHierarchyIncomingCallsParams,
1049) -> Result<Option<Vec<CallHierarchyIncomingCall>>> { 1053) -> Result<Option<Vec<CallHierarchyIncomingCall>>> {
1050 let _p = profile("handle_call_hierarchy_incoming"); 1054 let _p = profile("handle_call_hierarchy_incoming");
1051 let item = params.item; 1055 let item = params.item;
1052 1056
1053 let doc = TextDocumentIdentifier::new(item.uri); 1057 let doc = TextDocumentIdentifier::new(item.uri);
1054 let frange = from_proto::file_range(&world, doc, item.range)?; 1058 let frange = from_proto::file_range(&snap, doc, item.range)?;
1055 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1059 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1056 1060
1057 let call_items = match world.analysis().incoming_calls(fpos)? { 1061 let call_items = match snap.analysis().incoming_calls(fpos)? {
1058 None => return Ok(None), 1062 None => return Ok(None),
1059 Some(it) => it, 1063 Some(it) => it,
1060 }; 1064 };
@@ -1063,8 +1067,8 @@ pub fn handle_call_hierarchy_incoming(
1063 1067
1064 for call_item in call_items.into_iter() { 1068 for call_item in call_items.into_iter() {
1065 let file_id = call_item.target.file_id(); 1069 let file_id = call_item.target.file_id();
1066 let line_index = world.analysis().file_line_index(file_id)?; 1070 let line_index = snap.analysis().file_line_index(file_id)?;
1067 let item = to_proto::call_hierarchy_item(&world, call_item.target)?; 1071 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1068 res.push(CallHierarchyIncomingCall { 1072 res.push(CallHierarchyIncomingCall {
1069 from: item, 1073 from: item,
1070 from_ranges: call_item 1074 from_ranges: call_item
@@ -1079,17 +1083,17 @@ pub fn handle_call_hierarchy_incoming(
1079} 1083}
1080 1084
1081pub fn handle_call_hierarchy_outgoing( 1085pub fn handle_call_hierarchy_outgoing(
1082 world: WorldSnapshot, 1086 snap: GlobalStateSnapshot,
1083 params: CallHierarchyOutgoingCallsParams, 1087 params: CallHierarchyOutgoingCallsParams,
1084) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> { 1088) -> Result<Option<Vec<CallHierarchyOutgoingCall>>> {
1085 let _p = profile("handle_call_hierarchy_outgoing"); 1089 let _p = profile("handle_call_hierarchy_outgoing");
1086 let item = params.item; 1090 let item = params.item;
1087 1091
1088 let doc = TextDocumentIdentifier::new(item.uri); 1092 let doc = TextDocumentIdentifier::new(item.uri);
1089 let frange = from_proto::file_range(&world, doc, item.range)?; 1093 let frange = from_proto::file_range(&snap, doc, item.range)?;
1090 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; 1094 let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
1091 1095
1092 let call_items = match world.analysis().outgoing_calls(fpos)? { 1096 let call_items = match snap.analysis().outgoing_calls(fpos)? {
1093 None => return Ok(None), 1097 None => return Ok(None),
1094 Some(it) => it, 1098 Some(it) => it,
1095 }; 1099 };
@@ -1098,8 +1102,8 @@ pub fn handle_call_hierarchy_outgoing(
1098 1102
1099 for call_item in call_items.into_iter() { 1103 for call_item in call_items.into_iter() {
1100 let file_id = call_item.target.file_id(); 1104 let file_id = call_item.target.file_id();
1101 let line_index = world.analysis().file_line_index(file_id)?; 1105 let line_index = snap.analysis().file_line_index(file_id)?;
1102 let item = to_proto::call_hierarchy_item(&world, call_item.target)?; 1106 let item = to_proto::call_hierarchy_item(&snap, call_item.target)?;
1103 res.push(CallHierarchyOutgoingCall { 1107 res.push(CallHierarchyOutgoingCall {
1104 to: item, 1108 to: item,
1105 from_ranges: call_item 1109 from_ranges: call_item
@@ -1114,31 +1118,31 @@ pub fn handle_call_hierarchy_outgoing(
1114} 1118}
1115 1119
1116pub fn handle_semantic_tokens( 1120pub fn handle_semantic_tokens(
1117 world: WorldSnapshot, 1121 snap: GlobalStateSnapshot,
1118 params: SemanticTokensParams, 1122 params: SemanticTokensParams,
1119) -> Result<Option<SemanticTokensResult>> { 1123) -> Result<Option<SemanticTokensResult>> {
1120 let _p = profile("handle_semantic_tokens"); 1124 let _p = profile("handle_semantic_tokens");
1121 1125
1122 let file_id = from_proto::file_id(&world, &params.text_document.uri)?; 1126 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1123 let text = world.analysis().file_text(file_id)?; 1127 let text = snap.analysis().file_text(file_id)?;
1124 let line_index = world.analysis().file_line_index(file_id)?; 1128 let line_index = snap.analysis().file_line_index(file_id)?;
1125 1129
1126 let highlights = world.analysis().highlight(file_id)?; 1130 let highlights = snap.analysis().highlight(file_id)?;
1127 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1131 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1128 Ok(Some(semantic_tokens.into())) 1132 Ok(Some(semantic_tokens.into()))
1129} 1133}
1130 1134
1131pub fn handle_semantic_tokens_range( 1135pub fn handle_semantic_tokens_range(
1132 world: WorldSnapshot, 1136 snap: GlobalStateSnapshot,
1133 params: SemanticTokensRangeParams, 1137 params: SemanticTokensRangeParams,
1134) -> Result<Option<SemanticTokensRangeResult>> { 1138) -> Result<Option<SemanticTokensRangeResult>> {
1135 let _p = profile("handle_semantic_tokens_range"); 1139 let _p = profile("handle_semantic_tokens_range");
1136 1140
1137 let frange = from_proto::file_range(&world, params.text_document, params.range)?; 1141 let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
1138 let text = world.analysis().file_text(frange.file_id)?; 1142 let text = snap.analysis().file_text(frange.file_id)?;
1139 let line_index = world.analysis().file_line_index(frange.file_id)?; 1143 let line_index = snap.analysis().file_line_index(frange.file_id)?;
1140 1144
1141 let highlights = world.analysis().highlight_range(frange)?; 1145 let highlights = snap.analysis().highlight_range(frange)?;
1142 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1146 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1143 Ok(Some(semantic_tokens.into())) 1147 Ok(Some(semantic_tokens.into()))
1144} 1148}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index fb33bdd5f..1da4d80ec 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -10,7 +10,8 @@ use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_vfs::LineEndings; 10use ra_vfs::LineEndings;
11 11
12use crate::{ 12use crate::{
13 cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result, 13 cargo_target_spec::CargoTargetSpec, global_state::GlobalStateSnapshot, lsp_ext,
14 semantic_tokens, Result,
14}; 15};
15 16
16pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { 17pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
@@ -384,41 +385,44 @@ pub(crate) fn folding_range(
384 } 385 }
385} 386}
386 387
387pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::Url> { 388pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> Result<lsp_types::Url> {
388 world.file_id_to_uri(file_id) 389 snap.file_id_to_uri(file_id)
389} 390}
390 391
391pub(crate) fn versioned_text_document_identifier( 392pub(crate) fn versioned_text_document_identifier(
392 world: &WorldSnapshot, 393 snap: &GlobalStateSnapshot,
393 file_id: FileId, 394 file_id: FileId,
394 version: Option<i64>, 395 version: Option<i64>,
395) -> Result<lsp_types::VersionedTextDocumentIdentifier> { 396) -> Result<lsp_types::VersionedTextDocumentIdentifier> {
396 let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(world, file_id)?, version }; 397 let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id)?, version };
397 Ok(res) 398 Ok(res)
398} 399}
399 400
400pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_types::Location> { 401pub(crate) fn location(
401 let url = url(world, frange.file_id)?; 402 snap: &GlobalStateSnapshot,
402 let line_index = world.analysis().file_line_index(frange.file_id)?; 403 frange: FileRange,
404) -> Result<lsp_types::Location> {
405 let url = url(snap, frange.file_id)?;
406 let line_index = snap.analysis().file_line_index(frange.file_id)?;
403 let range = range(&line_index, frange.range); 407 let range = range(&line_index, frange.range);
404 let loc = lsp_types::Location::new(url, range); 408 let loc = lsp_types::Location::new(url, range);
405 Ok(loc) 409 Ok(loc)
406} 410}
407 411
408pub(crate) fn location_link( 412pub(crate) fn location_link(
409 world: &WorldSnapshot, 413 snap: &GlobalStateSnapshot,
410 src: Option<FileRange>, 414 src: Option<FileRange>,
411 target: NavigationTarget, 415 target: NavigationTarget,
412) -> Result<lsp_types::LocationLink> { 416) -> Result<lsp_types::LocationLink> {
413 let origin_selection_range = match src { 417 let origin_selection_range = match src {
414 Some(src) => { 418 Some(src) => {
415 let line_index = world.analysis().file_line_index(src.file_id)?; 419 let line_index = snap.analysis().file_line_index(src.file_id)?;
416 let range = range(&line_index, src.range); 420 let range = range(&line_index, src.range);
417 Some(range) 421 Some(range)
418 } 422 }
419 None => None, 423 None => None,
420 }; 424 };
421 let (target_uri, target_range, target_selection_range) = location_info(world, target)?; 425 let (target_uri, target_range, target_selection_range) = location_info(snap, target)?;
422 let res = lsp_types::LocationLink { 426 let res = lsp_types::LocationLink {
423 origin_selection_range, 427 origin_selection_range,
424 target_uri, 428 target_uri,
@@ -429,12 +433,12 @@ pub(crate) fn location_link(
429} 433}
430 434
431fn location_info( 435fn location_info(
432 world: &WorldSnapshot, 436 snap: &GlobalStateSnapshot,
433 target: NavigationTarget, 437 target: NavigationTarget,
434) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { 438) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
435 let line_index = world.analysis().file_line_index(target.file_id())?; 439 let line_index = snap.analysis().file_line_index(target.file_id())?;
436 440
437 let target_uri = url(world, target.file_id())?; 441 let target_uri = url(snap, target.file_id())?;
438 let target_range = range(&line_index, target.full_range()); 442 let target_range = range(&line_index, target.full_range());
439 let target_selection_range = 443 let target_selection_range =
440 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); 444 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range);
@@ -442,14 +446,14 @@ fn location_info(
442} 446}
443 447
444pub(crate) fn goto_definition_response( 448pub(crate) fn goto_definition_response(
445 world: &WorldSnapshot, 449 snap: &GlobalStateSnapshot,
446 src: Option<FileRange>, 450 src: Option<FileRange>,
447 targets: Vec<NavigationTarget>, 451 targets: Vec<NavigationTarget>,
448) -> Result<lsp_types::GotoDefinitionResponse> { 452) -> Result<lsp_types::GotoDefinitionResponse> {
449 if world.config.client_caps.location_link { 453 if snap.config.client_caps.location_link {
450 let links = targets 454 let links = targets
451 .into_iter() 455 .into_iter()
452 .map(|nav| location_link(world, src, nav)) 456 .map(|nav| location_link(snap, src, nav))
453 .collect::<Result<Vec<_>>>()?; 457 .collect::<Result<Vec<_>>>()?;
454 Ok(links.into()) 458 Ok(links.into())
455 } else { 459 } else {
@@ -457,7 +461,7 @@ pub(crate) fn goto_definition_response(
457 .into_iter() 461 .into_iter()
458 .map(|nav| { 462 .map(|nav| {
459 location( 463 location(
460 world, 464 snap,
461 FileRange { 465 FileRange {
462 file_id: nav.file_id(), 466 file_id: nav.file_id(),
463 range: nav.focus_range().unwrap_or(nav.range()), 467 range: nav.focus_range().unwrap_or(nav.range()),
@@ -470,13 +474,13 @@ pub(crate) fn goto_definition_response(
470} 474}
471 475
472pub(crate) fn snippet_text_document_edit( 476pub(crate) fn snippet_text_document_edit(
473 world: &WorldSnapshot, 477 snap: &GlobalStateSnapshot,
474 is_snippet: bool, 478 is_snippet: bool,
475 source_file_edit: SourceFileEdit, 479 source_file_edit: SourceFileEdit,
476) -> Result<lsp_ext::SnippetTextDocumentEdit> { 480) -> Result<lsp_ext::SnippetTextDocumentEdit> {
477 let text_document = versioned_text_document_identifier(world, source_file_edit.file_id, None)?; 481 let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None)?;
478 let line_index = world.analysis().file_line_index(source_file_edit.file_id)?; 482 let line_index = snap.analysis().file_line_index(source_file_edit.file_id)?;
479 let line_endings = world.file_line_endings(source_file_edit.file_id); 483 let line_endings = snap.file_line_endings(source_file_edit.file_id);
480 let edits = source_file_edit 484 let edits = source_file_edit
481 .edit 485 .edit
482 .into_iter() 486 .into_iter()
@@ -486,17 +490,17 @@ pub(crate) fn snippet_text_document_edit(
486} 490}
487 491
488pub(crate) fn resource_op( 492pub(crate) fn resource_op(
489 world: &WorldSnapshot, 493 snap: &GlobalStateSnapshot,
490 file_system_edit: FileSystemEdit, 494 file_system_edit: FileSystemEdit,
491) -> Result<lsp_types::ResourceOp> { 495) -> Result<lsp_types::ResourceOp> {
492 let res = match file_system_edit { 496 let res = match file_system_edit {
493 FileSystemEdit::CreateFile { source_root, path } => { 497 FileSystemEdit::CreateFile { source_root, path } => {
494 let uri = world.path_to_uri(source_root, &path)?; 498 let uri = snap.path_to_uri(source_root, &path)?;
495 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) 499 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None })
496 } 500 }
497 FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { 501 FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => {
498 let old_uri = world.file_id_to_uri(src)?; 502 let old_uri = snap.file_id_to_uri(src)?;
499 let new_uri = world.path_to_uri(dst_source_root, &dst_path)?; 503 let new_uri = snap.path_to_uri(dst_source_root, &dst_path)?;
500 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) 504 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None })
501 } 505 }
502 }; 506 };
@@ -504,16 +508,16 @@ pub(crate) fn resource_op(
504} 508}
505 509
506pub(crate) fn snippet_workspace_edit( 510pub(crate) fn snippet_workspace_edit(
507 world: &WorldSnapshot, 511 snap: &GlobalStateSnapshot,
508 source_change: SourceChange, 512 source_change: SourceChange,
509) -> Result<lsp_ext::SnippetWorkspaceEdit> { 513) -> Result<lsp_ext::SnippetWorkspaceEdit> {
510 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); 514 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
511 for op in source_change.file_system_edits { 515 for op in source_change.file_system_edits {
512 let op = resource_op(&world, op)?; 516 let op = resource_op(&snap, op)?;
513 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); 517 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
514 } 518 }
515 for edit in source_change.source_file_edits { 519 for edit in source_change.source_file_edits {
516 let edit = snippet_text_document_edit(&world, source_change.is_snippet, edit)?; 520 let edit = snippet_text_document_edit(&snap, source_change.is_snippet, edit)?;
517 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); 521 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit));
518 } 522 }
519 let workspace_edit = 523 let workspace_edit =
@@ -522,11 +526,11 @@ pub(crate) fn snippet_workspace_edit(
522} 526}
523 527
524pub(crate) fn workspace_edit( 528pub(crate) fn workspace_edit(
525 world: &WorldSnapshot, 529 snap: &GlobalStateSnapshot,
526 source_change: SourceChange, 530 source_change: SourceChange,
527) -> Result<lsp_types::WorkspaceEdit> { 531) -> Result<lsp_types::WorkspaceEdit> {
528 assert!(!source_change.is_snippet); 532 assert!(!source_change.is_snippet);
529 snippet_workspace_edit(world, source_change).map(|it| it.into()) 533 snippet_workspace_edit(snap, source_change).map(|it| it.into())
530} 534}
531 535
532impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { 536impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
@@ -565,13 +569,13 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
565} 569}
566 570
567pub fn call_hierarchy_item( 571pub fn call_hierarchy_item(
568 world: &WorldSnapshot, 572 snap: &GlobalStateSnapshot,
569 target: NavigationTarget, 573 target: NavigationTarget,
570) -> Result<lsp_types::CallHierarchyItem> { 574) -> Result<lsp_types::CallHierarchyItem> {
571 let name = target.name().to_string(); 575 let name = target.name().to_string();
572 let detail = target.description().map(|it| it.to_string()); 576 let detail = target.description().map(|it| it.to_string());
573 let kind = symbol_kind(target.kind()); 577 let kind = symbol_kind(target.kind());
574 let (uri, range, selection_range) = location_info(world, target)?; 578 let (uri, range, selection_range) = location_info(snap, target)?;
575 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) 579 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range })
576} 580}
577 581
@@ -620,14 +624,14 @@ fn main() <fold>{
620} 624}
621 625
622pub(crate) fn unresolved_code_action( 626pub(crate) fn unresolved_code_action(
623 world: &WorldSnapshot, 627 snap: &GlobalStateSnapshot,
624 assist: Assist, 628 assist: Assist,
625 index: usize, 629 index: usize,
626) -> Result<lsp_ext::CodeAction> { 630) -> Result<lsp_ext::CodeAction> {
627 let res = lsp_ext::CodeAction { 631 let res = lsp_ext::CodeAction {
628 title: assist.label, 632 title: assist.label,
629 id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())), 633 id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())),
630 group: assist.group.filter(|_| world.config.client_caps.code_action_group).map(|gr| gr.0), 634 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
631 kind: Some(String::new()), 635 kind: Some(String::new()),
632 edit: None, 636 edit: None,
633 command: None, 637 command: None,
@@ -636,25 +640,25 @@ pub(crate) fn unresolved_code_action(
636} 640}
637 641
638pub(crate) fn resolved_code_action( 642pub(crate) fn resolved_code_action(
639 world: &WorldSnapshot, 643 snap: &GlobalStateSnapshot,
640 assist: ResolvedAssist, 644 assist: ResolvedAssist,
641) -> Result<lsp_ext::CodeAction> { 645) -> Result<lsp_ext::CodeAction> {
642 let change = assist.source_change; 646 let change = assist.source_change;
643 unresolved_code_action(world, assist.assist, 0).and_then(|it| { 647 unresolved_code_action(snap, assist.assist, 0).and_then(|it| {
644 Ok(lsp_ext::CodeAction { 648 Ok(lsp_ext::CodeAction {
645 id: None, 649 id: None,
646 edit: Some(snippet_workspace_edit(world, change)?), 650 edit: Some(snippet_workspace_edit(snap, change)?),
647 ..it 651 ..it
648 }) 652 })
649 }) 653 })
650} 654}
651 655
652pub(crate) fn runnable( 656pub(crate) fn runnable(
653 world: &WorldSnapshot, 657 snap: &GlobalStateSnapshot,
654 file_id: FileId, 658 file_id: FileId,
655 runnable: Runnable, 659 runnable: Runnable,
656) -> Result<lsp_ext::Runnable> { 660) -> Result<lsp_ext::Runnable> {
657 let spec = CargoTargetSpec::for_file(world, file_id)?; 661 let spec = CargoTargetSpec::for_file(snap, file_id)?;
658 let target = spec.as_ref().map(|s| s.target.clone()); 662 let target = spec.as_ref().map(|s| s.target.clone());
659 let (cargo_args, executable_args) = 663 let (cargo_args, executable_args) =
660 CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; 664 CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?;
@@ -667,14 +671,14 @@ pub(crate) fn runnable(
667 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) 671 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
668 } 672 }
669 }; 673 };
670 let location = location_link(world, None, runnable.nav)?; 674 let location = location_link(snap, None, runnable.nav)?;
671 675
672 Ok(lsp_ext::Runnable { 676 Ok(lsp_ext::Runnable {
673 label, 677 label,
674 location: Some(location), 678 location: Some(location),
675 kind: lsp_ext::RunnableKind::Cargo, 679 kind: lsp_ext::RunnableKind::Cargo,
676 args: lsp_ext::CargoRunnable { 680 args: lsp_ext::CargoRunnable {
677 workspace_root: world.workspace_root_for(file_id).map(|root| root.to_owned()), 681 workspace_root: snap.workspace_root_for(file_id).map(|root| root.to_owned()),
678 cargo_args, 682 cargo_args,
679 executable_args, 683 executable_args,
680 }, 684 },
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index e18f973b8..ad3476310 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -59,55 +59,6 @@ use std::collections::Spam;
59} 59}
60 60
61#[test] 61#[test]
62fn test_runnables_no_project() {
63 if skip_slow_tests() {
64 return;
65 }
66
67 let server = project(
68 r"
69//- lib.rs
70#[test]
71fn foo() {
72}
73",
74 );
75 server.wait_until_workspace_is_loaded();
76 server.request::<Runnables>(
77 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
78 json!([
79 {
80 "args": {
81 "cargoArgs": ["test"],
82 "executableArgs": ["foo", "--nocapture"],
83 },
84 "kind": "cargo",
85 "label": "test foo",
86 "location": {
87 "targetRange": {
88 "end": { "character": 1, "line": 2 },
89 "start": { "character": 0, "line": 0 }
90 },
91 "targetSelectionRange": {
92 "end": { "character": 6, "line": 1 },
93 "start": { "character": 3, "line": 1 }
94 },
95 "targetUri": "file:///[..]/lib.rs"
96 }
97 },
98 {
99 "args": {
100 "cargoArgs": ["check", "--workspace"],
101 "executableArgs": [],
102 },
103 "kind": "cargo",
104 "label": "cargo check --workspace"
105 }
106 ]),
107 );
108}
109
110#[test]
111fn test_runnables_project() { 62fn test_runnables_project() {
112 if skip_slow_tests() { 63 if skip_slow_tests() {
113 return; 64 return;
@@ -347,6 +298,7 @@ fn main() {}
347 } 298 }
348 ] 299 ]
349 }, 300 },
301 "kind": "quickfix",
350 "title": "Create module" 302 "title": "Create module"
351 }]), 303 }]),
352 ); 304 );
@@ -379,8 +331,7 @@ fn test_missing_module_code_action_in_json_project() {
379 "root_module": path.join("src/lib.rs"), 331 "root_module": path.join("src/lib.rs"),
380 "deps": [], 332 "deps": [],
381 "edition": "2015", 333 "edition": "2015",
382 "atom_cfgs": [], 334 "cfg": [ "cfg_atom_1", "feature=cfg_1"],
383 "key_value_cfgs": {}
384 } ] 335 } ]
385 }); 336 });
386 337
@@ -418,6 +369,7 @@ fn main() {{}}
418 } 369 }
419 ] 370 ]
420 }, 371 },
372 "kind": "quickfix",
421 "title": "Create module" 373 "title": "Create module"
422 }]), 374 }]),
423 ); 375 );
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 =
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 71a57fba2..c0356344c 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -124,3 +124,8 @@ pub fn replace(buf: &mut String, from: char, to: &str) {
124 // FIXME: do this in place. 124 // FIXME: do this in place.
125 *buf = buf.replace(from, to) 125 *buf = buf.replace(from, to)
126} 126}
127
128pub fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
129 let idx = haystack.find(delim)?;
130 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
131}
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 4d185b01c..8840bf36a 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -14,4 +14,5 @@ serde_json = "1.0.48"
14relative-path = "1.0.0" 14relative-path = "1.0.0"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16 16
17ra_cfg = { path = "../ra_cfg" } \ No newline at end of file 17ra_cfg = { path = "../ra_cfg" }
18stdx = { path = "../stdx" } \ No newline at end of file
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 1bd97215c..2141bfc20 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -15,6 +15,7 @@ use std::{
15}; 15};
16 16
17pub use ra_cfg::CfgOptions; 17pub use ra_cfg::CfgOptions;
18use stdx::split1;
18 19
19pub use relative_path::{RelativePath, RelativePathBuf}; 20pub use relative_path::{RelativePath, RelativePathBuf};
20pub use rustc_hash::FxHashMap; 21pub use rustc_hash::FxHashMap;
@@ -332,11 +333,6 @@ fn parse_meta(meta: &str) -> FixtureMeta {
332 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env }) 333 FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env })
333} 334}
334 335
335fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
336 let idx = haystack.find(delim)?;
337 Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..]))
338}
339
340/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. 336/// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines.
341/// This allows fixtures to start off in a different indentation, e.g. to align the first line with 337/// This allows fixtures to start off in a different indentation, e.g. to align the first line with
342/// the other lines visually: 338/// the other lines visually: