diff options
50 files changed, 1191 insertions, 1829 deletions
diff --git a/.gitignore b/.gitignore index dab51647d..aef0fac33 100644 --- a/.gitignore +++ b/.gitignore | |||
@@ -8,3 +8,5 @@ crates/*/target | |||
8 | *.iml | 8 | *.iml |
9 | .vscode/settings.json | 9 | .vscode/settings.json |
10 | *.html | 10 | *.html |
11 | generated_assists.adoc | ||
12 | generated_features.adoc | ||
diff --git a/Cargo.lock b/Cargo.lock index af27bfc85..5f88ad0c4 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -462,9 +462,9 @@ dependencies = [ | |||
462 | 462 | ||
463 | [[package]] | 463 | [[package]] |
464 | name = "indexmap" | 464 | name = "indexmap" |
465 | version = "1.3.2" | 465 | version = "1.4.0" |
466 | source = "registry+https://github.com/rust-lang/crates.io-index" | 466 | source = "registry+https://github.com/rust-lang/crates.io-index" |
467 | checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" | 467 | checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" |
468 | dependencies = [ | 468 | dependencies = [ |
469 | "autocfg", | 469 | "autocfg", |
470 | ] | 470 | ] |
@@ -809,9 +809,9 @@ dependencies = [ | |||
809 | 809 | ||
810 | [[package]] | 810 | [[package]] |
811 | name = "paste" | 811 | name = "paste" |
812 | version = "0.1.15" | 812 | version = "0.1.16" |
813 | source = "registry+https://github.com/rust-lang/crates.io-index" | 813 | source = "registry+https://github.com/rust-lang/crates.io-index" |
814 | checksum = "d53181dcd37421c08d3b69f887784956674d09c3f9a47a04fece2b130a5b346b" | 814 | checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" |
815 | dependencies = [ | 815 | dependencies = [ |
816 | "paste-impl", | 816 | "paste-impl", |
817 | "proc-macro-hack", | 817 | "proc-macro-hack", |
@@ -819,9 +819,9 @@ dependencies = [ | |||
819 | 819 | ||
820 | [[package]] | 820 | [[package]] |
821 | name = "paste-impl" | 821 | name = "paste-impl" |
822 | version = "0.1.15" | 822 | version = "0.1.16" |
823 | source = "registry+https://github.com/rust-lang/crates.io-index" | 823 | source = "registry+https://github.com/rust-lang/crates.io-index" |
824 | checksum = "05ca490fa1c034a71412b4d1edcb904ec5a0981a4426c9eb2128c0fda7a68d17" | 824 | checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" |
825 | dependencies = [ | 825 | dependencies = [ |
826 | "proc-macro-hack", | 826 | "proc-macro-hack", |
827 | "proc-macro2", | 827 | "proc-macro2", |
@@ -871,9 +871,9 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" | |||
871 | 871 | ||
872 | [[package]] | 872 | [[package]] |
873 | name = "proc-macro2" | 873 | name = "proc-macro2" |
874 | version = "1.0.17" | 874 | version = "1.0.18" |
875 | source = "registry+https://github.com/rust-lang/crates.io-index" | 875 | source = "registry+https://github.com/rust-lang/crates.io-index" |
876 | checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" | 876 | checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" |
877 | dependencies = [ | 877 | dependencies = [ |
878 | "unicode-xid", | 878 | "unicode-xid", |
879 | ] | 879 | ] |
@@ -1401,9 +1401,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" | |||
1401 | 1401 | ||
1402 | [[package]] | 1402 | [[package]] |
1403 | name = "ryu" | 1403 | name = "ryu" |
1404 | version = "1.0.4" | 1404 | version = "1.0.5" |
1405 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1405 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1406 | checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" | 1406 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" |
1407 | 1407 | ||
1408 | [[package]] | 1408 | [[package]] |
1409 | name = "salsa" | 1409 | name = "salsa" |
@@ -1577,9 +1577,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" | |||
1577 | 1577 | ||
1578 | [[package]] | 1578 | [[package]] |
1579 | name = "syn" | 1579 | name = "syn" |
1580 | version = "1.0.29" | 1580 | version = "1.0.30" |
1581 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1582 | checksum = "bb37da98a55b1d08529362d9cbb863be17556873df2585904ab9d2bc951291d0" | 1582 | checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" |
1583 | dependencies = [ | 1583 | dependencies = [ |
1584 | "proc-macro2", | 1584 | "proc-macro2", |
1585 | "quote", | 1585 | "quote", |
@@ -1640,6 +1640,7 @@ dependencies = [ | |||
1640 | "relative-path", | 1640 | "relative-path", |
1641 | "rustc-hash", | 1641 | "rustc-hash", |
1642 | "serde_json", | 1642 | "serde_json", |
1643 | "stdx", | ||
1643 | "text-size", | 1644 | "text-size", |
1644 | ] | 1645 | ] |
1645 | 1646 | ||
@@ -1798,9 +1799,9 @@ dependencies = [ | |||
1798 | 1799 | ||
1799 | [[package]] | 1800 | [[package]] |
1800 | name = "yaml-rust" | 1801 | name = "yaml-rust" |
1801 | version = "0.4.3" | 1802 | version = "0.4.4" |
1802 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1803 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1803 | checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" | 1804 | checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" |
1804 | dependencies = [ | 1805 | dependencies = [ |
1805 | "linked-hash-map", | 1806 | "linked-hash-map", |
1806 | ] | 1807 | ] |
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 | ||
73 | pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { | 80 | pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation> |
74 | node.doc_comment_text().map(|it| Documentation::new(&it)) | 81 | where |
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 | |||
90 | fn 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 | |||
106 | fn 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)] | ||
130 | mod 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::{ | |||
10 | use hir::{Docs, Documentation, HasSource, HirDisplay}; | 10 | use hir::{Docs, Documentation, HasSource, HirDisplay}; |
11 | use ra_ide_db::RootDatabase; | 11 | use ra_ide_db::RootDatabase; |
12 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; | 12 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; |
13 | use stdx::SepBy; | 13 | use stdx::{split1, SepBy}; |
14 | 14 | ||
15 | use crate::display::{generic_parameters, where_predicates}; | 15 | use 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 @@ | |||
1 | use std::iter::once; | 1 | use std::iter::once; |
2 | 2 | ||
3 | use hir::{ | 3 | use 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 | }; |
7 | use itertools::Itertools; | 7 | use itertools::Itertools; |
8 | use ra_db::SourceDatabase; | 8 | use 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 | }; |
13 | use ra_syntax::{ | 13 | use 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 | ||
20 | use crate::{ | 15 | use 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> | ||
3 | body { margin: 0; } | ||
4 | pre { 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] | ||
263 | fn test_unsafe_highlighting() { | ||
264 | let (analysis, file_id) = single_file( | ||
265 | r#" | ||
266 | unsafe fn unsafe_fn() {} | ||
267 | |||
268 | struct HasUnsafeFn; | ||
269 | |||
270 | impl HasUnsafeFn { | ||
271 | unsafe fn unsafe_method(&self) {} | ||
272 | } | ||
273 | |||
274 | fn 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; | |||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | 5 | use rustc_hash::{FxHashMap, FxHashSet}; |
6 | use serde::Deserialize; | 6 | use serde::Deserialize; |
7 | 7 | ||
8 | /// Roots and crates that compose this Rust project. | ||
9 | #[derive(Clone, Debug, Deserialize)] | ||
10 | pub 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)] | 68 | mod tests { |
53 | pub 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::{ | |||
14 | use anyhow::{bail, Context, Result}; | 14 | use anyhow::{bail, Context, Result}; |
15 | use ra_cfg::CfgOptions; | 15 | use ra_cfg::CfgOptions; |
16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; | 16 | use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; |
17 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::{FxHashMap, FxHashSet}; |
18 | use serde_json::from_reader; | 18 | use serde_json::from_reader; |
19 | 19 | ||
20 | pub use crate::{ | 20 | pub use crate::{ |
@@ -32,6 +32,12 @@ pub enum ProjectWorkspace { | |||
32 | Json { project: JsonProject }, | 32 | Json { project: JsonProject }, |
33 | } | 33 | } |
34 | 34 | ||
35 | impl 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)] |
61 | pub enum ProjectRoot { | 67 | pub enum ProjectManifest { |
62 | ProjectJson(PathBuf), | 68 | ProjectJson(PathBuf), |
63 | CargoToml(PathBuf), | 69 | CargoToml(PathBuf), |
64 | } | 70 | } |
65 | 71 | ||
66 | impl ProjectRoot { | 72 | impl 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 | ||
133 | impl ProjectWorkspace { | 151 | impl 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 | |||
91 | impl 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 @@ | |||
4 | mod args; | 4 | mod args; |
5 | 5 | ||
6 | use lsp_server::Connection; | 6 | use lsp_server::Connection; |
7 | use rust_analyzer::{cli, config::Config, from_json, Result}; | 7 | use rust_analyzer::{ |
8 | cli, | ||
9 | config::{Config, LinkedProject}, | ||
10 | from_json, Result, | ||
11 | }; | ||
8 | 12 | ||
9 | use crate::args::HelpPrinted; | 13 | use crate::args::HelpPrinted; |
14 | use ra_project_model::ProjectManifest; | ||
10 | 15 | ||
11 | fn main() -> Result<()> { | 16 | fn 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; | |||
4 | use ra_ide::{FileId, RunnableKind, TestId}; | 4 | use ra_ide::{FileId, RunnableKind, TestId}; |
5 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; | 5 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; |
6 | 6 | ||
7 | use crate::{world::WorldSnapshot, Result}; | 7 | use 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}; | |||
8 | use ra_db::{ExternSourceId, FileId, SourceRootId}; | 8 | use ra_db::{ExternSourceId, FileId, SourceRootId}; |
9 | use ra_ide::{AnalysisChange, AnalysisHost}; | 9 | use ra_ide::{AnalysisChange, AnalysisHost}; |
10 | use ra_project_model::{ | 10 | use 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 | }; |
13 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; | 14 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; |
14 | use rustc_hash::{FxHashMap, FxHashSet}; | 15 | use 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}; | |||
12 | use lsp_types::ClientCapabilities; | 12 | use lsp_types::ClientCapabilities; |
13 | use ra_flycheck::FlycheckConfig; | 13 | use ra_flycheck::FlycheckConfig; |
14 | use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; | 14 | use ra_ide::{AssistConfig, CompletionConfig, InlayHintsConfig}; |
15 | use ra_project_model::CargoConfig; | 15 | use ra_project_model::{CargoConfig, JsonProject, ProjectManifest}; |
16 | use serde::Deserialize; | 16 | use serde::Deserialize; |
17 | 17 | ||
18 | #[derive(Debug, Clone)] | 18 | #[derive(Debug, Clone)] |
19 | pub struct Config { | 19 | pub 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)] | ||
43 | pub enum LinkedProject { | ||
44 | ProjectManifest(ProjectManifest), | ||
45 | JsonProject(JsonProject), | ||
46 | } | ||
47 | |||
48 | impl From<ProjectManifest> for LinkedProject { | ||
49 | fn from(v: ProjectManifest) -> Self { | ||
50 | LinkedProject::ProjectManifest(v) | ||
51 | } | ||
52 | } | ||
53 | |||
54 | impl 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)] | ||
351 | enum 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}; | |||
3 | use ra_ide::{LineCol, LineIndex}; | 3 | use ra_ide::{LineCol, LineIndex}; |
4 | use ra_syntax::{TextRange, TextSize}; | 4 | use ra_syntax::{TextRange, TextSize}; |
5 | 5 | ||
6 | use crate::{world::WorldSnapshot, Result}; | 6 | use crate::{global_state::GlobalStateSnapshot, Result}; |
7 | 7 | ||
8 | pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> TextSize { | 8 | pub(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 | ||
19 | pub(crate) fn file_id(world: &WorldSnapshot, url: &lsp_types::Url) -> Result<FileId> { | 19 | pub(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 | ||
23 | pub(crate) fn file_position( | 23 | pub(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 | ||
33 | pub(crate) fn file_range( | 33 | pub(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)] |
59 | pub struct WorldState { | 59 | pub 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. |
73 | pub struct WorldSnapshot { | 73 | pub 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 | ||
82 | impl WorldState { | 82 | impl 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 | ||
278 | impl WorldSnapshot { | 282 | impl 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; | |||
26 | mod markdown; | 26 | mod markdown; |
27 | pub mod lsp_ext; | 27 | pub mod lsp_ext; |
28 | pub mod config; | 28 | pub mod config; |
29 | mod world; | 29 | mod global_state; |
30 | mod diagnostics; | 30 | mod diagnostics; |
31 | mod semantic_tokens; | 31 | mod 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 | ||
20 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; | 19 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; |
21 | use itertools::Itertools; | ||
22 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 20 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
23 | use lsp_types::{ | 21 | use lsp_types::{ |
24 | DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, | 22 | DidChangeTextDocumentParams, NumberOrString, TextDocumentContentChangeEvent, WorkDoneProgress, |
@@ -36,14 +34,15 @@ use serde::{de::DeserializeOwned, Serialize}; | |||
36 | use threadpool::ThreadPool; | 34 | use threadpool::ThreadPool; |
37 | 35 | ||
38 | use crate::{ | 36 | use 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 | ||
70 | impl Error for LspError {} | 69 | impl Error for LspError {} |
71 | 70 | ||
72 | pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> { | 71 | pub 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 | ||
487 | fn on_request( | 479 | fn 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 | ||
554 | fn on_notification( | 546 | fn 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 | ||
728 | fn on_check_task( | 720 | fn 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 | ||
808 | fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state: &mut WorldState) { | 800 | fn 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) { | |||
880 | struct PoolDispatcher<'a> { | 872 | struct 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 | ||
1014 | fn update_file_notifications_on_threadpool( | 1009 | fn 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 | ||
41 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | 40 | pub 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 | ||
53 | pub fn handle_syntax_tree( | 52 | pub 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, ¶ms.text_document.uri)?; | 57 | let id = from_proto::file_id(&snap, ¶ms.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 | ||
65 | pub fn handle_expand_macro( | 64 | pub 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, ¶ms.text_document.uri)?; | 69 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
78 | pub fn handle_selection_range( | 77 | pub 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, ¶ms.text_document.uri)?; | 82 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
121 | pub fn handle_matching_brace( | 120 | pub 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, ¶ms.text_document.uri)?; | 125 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
143 | pub fn handle_join_lines( | 142 | pub 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, ¶ms.text_document.uri)?; | 147 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
166 | pub fn handle_on_enter( | 165 | pub 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`. |
183 | pub fn handle_on_type_formatting( | 182 | pub 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 | ||
222 | pub fn handle_document_symbol( | 221 | pub 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, ¶ms.text_document.uri)?; | 226 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
290 | pub fn handle_workspace_symbol( | 289 | pub 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 | ||
334 | pub fn handle_goto_definition( | 333 | pub 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 | ||
349 | pub fn handle_goto_implementation( | 348 | pub 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 | ||
364 | pub fn handle_goto_type_definition( | 363 | pub 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 | ||
379 | pub fn handle_parent_module( | 378 | pub 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 | ||
390 | pub fn handle_runnables( | 389 | pub 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, ¶ms.text_document.uri)?; | 394 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
455 | pub fn handle_completion( | 454 | pub 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 | ||
497 | pub fn handle_folding_range( | 496 | pub 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, ¶ms.text_document.uri)?; | 501 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
514 | pub fn handle_signature_help( | 513 | pub 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 | ||
538 | pub fn handle_hover(world: WorldSnapshot, params: lsp_types::HoverParams) -> Result<Option<Hover>> { | 537 | pub 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 | ||
557 | pub fn handle_prepare_rename( | 559 | pub 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 | ||
575 | pub fn handle_rename(world: WorldSnapshot, params: RenameParams) -> Result<Option<WorkspaceEdit>> { | 577 | pub 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 | ||
596 | pub fn handle_references( | 601 | pub 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 | ||
623 | pub fn handle_formatting( | 628 | pub 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, ¶ms.text_document.uri)?; | 633 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
699 | fn handle_fixes( | 704 | fn 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, ¶ms.text_document.uri)?; | 709 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
738 | pub fn handle_code_action( | 743 | pub 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, ¶ms.text_document.uri)?; | 755 | let file_id = from_proto::file_id(&snap, ¶ms.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, ¶ms, &mut res)?; | 761 | handle_fixes(&snap, ¶ms, &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 | ||
776 | pub fn handle_resolve_code_action( | 778 | pub 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, ¶ms.code_action_params.text_document.uri)?; | 783 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
795 | pub fn handle_code_lens( | 797 | pub 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, ¶ms.text_document.uri)?; | 809 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
907 | pub fn handle_code_lens_resolve(world: WorldSnapshot, code_lens: CodeLens) -> Result<CodeLens> { | 908 | pub 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 | ||
952 | pub fn handle_document_highlight( | 956 | pub 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 | ||
979 | pub fn handle_ssr( | 983 | pub 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(¶ms.query, params.parse_only)??; | 989 | snap.analysis().structural_search_replace(¶ms.query, params.parse_only)??; |
986 | to_proto::workspace_edit(&world, source_change) | 990 | to_proto::workspace_edit(&snap, source_change) |
987 | } | 991 | } |
988 | 992 | ||
989 | pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<DiagnosticTask> { | 993 | pub 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 | ||
1009 | pub fn handle_inlay_hints( | 1013 | pub 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, ¶ms.text_document.uri)?; | 1018 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
1024 | pub fn handle_call_hierarchy_prepare( | 1028 | pub 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 | ||
1046 | pub fn handle_call_hierarchy_incoming( | 1050 | pub 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 | ||
1081 | pub fn handle_call_hierarchy_outgoing( | 1085 | pub 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 | ||
1116 | pub fn handle_semantic_tokens( | 1120 | pub 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, ¶ms.text_document.uri)?; | 1126 | let file_id = from_proto::file_id(&snap, ¶ms.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 | ||
1131 | pub fn handle_semantic_tokens_range( | 1135 | pub 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}; | |||
10 | use ra_vfs::LineEndings; | 10 | use ra_vfs::LineEndings; |
11 | 11 | ||
12 | use crate::{ | 12 | use 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 | ||
16 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { | 17 | pub(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 | ||
387 | pub(crate) fn url(world: &WorldSnapshot, file_id: FileId) -> Result<lsp_types::Url> { | 388 | pub(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 | ||
391 | pub(crate) fn versioned_text_document_identifier( | 392 | pub(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 | ||
400 | pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result<lsp_types::Location> { | 401 | pub(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 | ||
408 | pub(crate) fn location_link( | 412 | pub(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 | ||
431 | fn location_info( | 435 | fn 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 | ||
444 | pub(crate) fn goto_definition_response( | 448 | pub(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 | ||
472 | pub(crate) fn snippet_text_document_edit( | 476 | pub(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 | ||
488 | pub(crate) fn resource_op( | 492 | pub(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 | ||
506 | pub(crate) fn snippet_workspace_edit( | 510 | pub(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 | ||
524 | pub(crate) fn workspace_edit( | 528 | pub(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 | ||
532 | impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | 536 | impl 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 | ||
567 | pub fn call_hierarchy_item( | 571 | pub 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 | ||
622 | pub(crate) fn unresolved_code_action( | 626 | pub(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 | ||
638 | pub(crate) fn resolved_code_action( | 642 | pub(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 | ||
652 | pub(crate) fn runnable( | 656 | pub(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] |
62 | fn test_runnables_no_project() { | ||
63 | if skip_slow_tests() { | ||
64 | return; | ||
65 | } | ||
66 | |||
67 | let server = project( | ||
68 | r" | ||
69 | //- lib.rs | ||
70 | #[test] | ||
71 | fn 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] | ||
111 | fn test_runnables_project() { | 62 | fn 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}; | |||
19 | use tempfile::TempDir; | 19 | use tempfile::TempDir; |
20 | use test_utils::{find_mismatch, parse_fixture}; | 20 | use test_utils::{find_mismatch, parse_fixture}; |
21 | 21 | ||
22 | use ra_project_model::ProjectManifest; | ||
22 | use rust_analyzer::{ | 23 | use 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 | ||
111 | impl Server { | 122 | impl 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 | |||
128 | pub 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" | |||
14 | relative-path = "1.0.0" | 14 | relative-path = "1.0.0" |
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | 16 | ||
17 | ra_cfg = { path = "../ra_cfg" } \ No newline at end of file | 17 | ra_cfg = { path = "../ra_cfg" } |
18 | stdx = { 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 | ||
17 | pub use ra_cfg::CfgOptions; | 17 | pub use ra_cfg::CfgOptions; |
18 | use stdx::split1; | ||
18 | 19 | ||
19 | pub use relative_path::{RelativePath, RelativePathBuf}; | 20 | pub use relative_path::{RelativePath, RelativePathBuf}; |
20 | pub use rustc_hash::FxHashMap; | 21 | pub 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 | ||
335 | fn 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: |
diff --git a/docs/dev/README.md b/docs/dev/README.md index 65cc9fc12..1de5a2aab 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md | |||
@@ -30,7 +30,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0 | |||
30 | 30 | ||
31 | * [good-first-issue](https://github.com/rust-analyzer/rust-analyzer/labels/good%20first%20issue) | 31 | * [good-first-issue](https://github.com/rust-analyzer/rust-analyzer/labels/good%20first%20issue) |
32 | are good issues to get into the project. | 32 | are good issues to get into the project. |
33 | * [E-mentor](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-mentor) | 33 | * [E-has-instructions](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-has-instructions) |
34 | issues have links to the code in question and tests. | 34 | issues have links to the code in question and tests. |
35 | * [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), | 35 | * [E-easy](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy), |
36 | [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), | 36 | [E-medium](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3AE-medium), |
@@ -117,6 +117,109 @@ Additionally, I use `cargo run --release -p rust-analyzer -- analysis-stats | |||
117 | path/to/some/rust/crate` to run a batch analysis. This is primarily useful for | 117 | path/to/some/rust/crate` to run a batch analysis. This is primarily useful for |
118 | performance optimizations, or for bug minimization. | 118 | performance optimizations, or for bug minimization. |
119 | 119 | ||
120 | # Code Style & Review Process | ||
121 | |||
122 | Our approach to "clean code" is two fold: | ||
123 | |||
124 | * We generally don't block PRs on style changes. | ||
125 | * At the same time, all code in rust-analyzer is constantly refactored. | ||
126 | |||
127 | It is explicitly OK for reviewer to flag only some nits in the PR, and than send a follow up cleanup PR for things which are easier to explain by example, cc-ing the original author. | ||
128 | Sending small cleanup PRs (like rename a single local variable) is encouraged. | ||
129 | |||
130 | ## Scale of Changes | ||
131 | |||
132 | Everyone knows that it's better to send small & focused pull requests. | ||
133 | The problem is, sometimes you *have* to, eg, rewrite the whole compiler, and that just doesn't fit into a set of isolated PRs. | ||
134 | |||
135 | The main thing too keep an eye on is the boundaries between various components. | ||
136 | There are three kinds of changes: | ||
137 | |||
138 | 1. Internals of a single component are changed. | ||
139 | Specifically, you don't change any `pub` items. | ||
140 | A good example here would be an addition of a new assist. | ||
141 | |||
142 | 2. API of a component is expanded. | ||
143 | Specifically, you add a new `pub` function which wasn't there before. | ||
144 | A good example here would be expansion of assist API, for example, to implement lazy assists or assists groups. | ||
145 | |||
146 | 3. A new dependency between components is introduced. | ||
147 | Specifically, you add a `pub use` reexport from another crate or you add a new line to `[dependencies]` section of `Cargo.toml`. | ||
148 | A good example here would be adding reference search capability to the assists crates. | ||
149 | |||
150 | For the first group, the change is generally merged as long as: | ||
151 | |||
152 | * it works for the happy case, | ||
153 | * it has tests, | ||
154 | * it doesn't panic for unhappy case. | ||
155 | |||
156 | For the second group, the change would be subjected to quite a bit of scrutiny and iteration. | ||
157 | The new API needs to be right (or at least easy to change later). | ||
158 | The actual implementation doesn't matter that much. | ||
159 | It's very important to minimize the amount of changed lines of code for changes of the second kind. | ||
160 | Often, you start doing change of the first kind, only to realise that you need to elevate to a change of the second kind. | ||
161 | In this case, we'll probably ask you to split API changes into a separate PR. | ||
162 | |||
163 | Changes of the third group should be pretty rare, so we don't specify any specific process for them. | ||
164 | That said, adding an innocent-looking `pub use` is a very simple way to break encapsulation, keep an eye on it! | ||
165 | |||
166 | Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate | ||
167 | https://www.tedinski.com/2018/02/06/system-boundaries.html | ||
168 | |||
169 | ## Order of Imports | ||
170 | |||
171 | We separate import groups with blank lines | ||
172 | |||
173 | ``` | ||
174 | mod x; | ||
175 | mod y; | ||
176 | |||
177 | use std::{ ... } | ||
178 | |||
179 | use crate_foo::{ ... } | ||
180 | use crate_bar::{ ... } | ||
181 | |||
182 | use crate::{} | ||
183 | |||
184 | use super::{} // but prefer `use crate::` | ||
185 | ``` | ||
186 | |||
187 | ## Order of Items | ||
188 | |||
189 | Optimize for the reader who sees the file for the first time, and wants to get the general idea about what's going on. | ||
190 | People read things from top to bottom, so place most important things first. | ||
191 | |||
192 | Specifically, if all items except one are private, always put the non-private item on top. | ||
193 | |||
194 | Put `struct`s and `enum`s first, functions and impls last. | ||
195 | |||
196 | Do | ||
197 | |||
198 | ``` | ||
199 | // Good | ||
200 | struct Foo { | ||
201 | bars: Vec<Bar> | ||
202 | } | ||
203 | |||
204 | struct Bar; | ||
205 | ``` | ||
206 | |||
207 | rather than | ||
208 | |||
209 | ``` | ||
210 | // Not as good | ||
211 | struct Bar; | ||
212 | |||
213 | struct Foo { | ||
214 | bars: Vec<Bar> | ||
215 | } | ||
216 | ``` | ||
217 | |||
218 | ## Documentation | ||
219 | |||
220 | For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. | ||
221 | If the line is too long, you want to split the sentence in two :-) | ||
222 | |||
120 | # Logging | 223 | # Logging |
121 | 224 | ||
122 | Logging is done by both rust-analyzer and VS Code, so it might be tricky to | 225 | Logging is done by both rust-analyzer and VS Code, so it might be tricky to |
diff --git a/docs/user/generated_assists.adoc b/docs/user/generated_assists.adoc deleted file mode 100644 index 4d2fb31d4..000000000 --- a/docs/user/generated_assists.adoc +++ /dev/null | |||
@@ -1,1015 +0,0 @@ | |||
1 | [discrete] | ||
2 | === `add_custom_impl` | ||
3 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_custom_impl.rs#L14[add_custom_impl.rs] | ||
4 | |||
5 | Adds impl block for derived trait. | ||
6 | |||
7 | .Before | ||
8 | ```rust | ||
9 | #[derive(Deb┃ug, Display)] | ||
10 | struct S; | ||
11 | ``` | ||
12 | |||
13 | .After | ||
14 | ```rust | ||
15 | #[derive(Display)] | ||
16 | struct S; | ||
17 | |||
18 | impl Debug for S { | ||
19 | $0 | ||
20 | } | ||
21 | ``` | ||
22 | |||
23 | |||
24 | [discrete] | ||
25 | === `add_derive` | ||
26 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_derive.rs#L9[add_derive.rs] | ||
27 | |||
28 | Adds a new `#[derive()]` clause to a struct or enum. | ||
29 | |||
30 | .Before | ||
31 | ```rust | ||
32 | struct Point { | ||
33 | x: u32, | ||
34 | y: u32,┃ | ||
35 | } | ||
36 | ``` | ||
37 | |||
38 | .After | ||
39 | ```rust | ||
40 | #[derive($0)] | ||
41 | struct Point { | ||
42 | x: u32, | ||
43 | y: u32, | ||
44 | } | ||
45 | ``` | ||
46 | |||
47 | |||
48 | [discrete] | ||
49 | === `add_explicit_type` | ||
50 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_explicit_type.rs#L9[add_explicit_type.rs] | ||
51 | |||
52 | Specify type for a let binding. | ||
53 | |||
54 | .Before | ||
55 | ```rust | ||
56 | fn main() { | ||
57 | let x┃ = 92; | ||
58 | } | ||
59 | ``` | ||
60 | |||
61 | .After | ||
62 | ```rust | ||
63 | fn main() { | ||
64 | let x: i32 = 92; | ||
65 | } | ||
66 | ``` | ||
67 | |||
68 | |||
69 | [discrete] | ||
70 | === `add_from_impl_for_enum` | ||
71 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs#L7[add_from_impl_for_enum.rs] | ||
72 | |||
73 | Adds a From impl for an enum variant with one tuple field. | ||
74 | |||
75 | .Before | ||
76 | ```rust | ||
77 | enum A { ┃One(u32) } | ||
78 | ``` | ||
79 | |||
80 | .After | ||
81 | ```rust | ||
82 | enum A { One(u32) } | ||
83 | |||
84 | impl From<u32> for A { | ||
85 | fn from(v: u32) -> Self { | ||
86 | A::One(v) | ||
87 | } | ||
88 | } | ||
89 | ``` | ||
90 | |||
91 | |||
92 | [discrete] | ||
93 | === `add_function` | ||
94 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_function.rs#L19[add_function.rs] | ||
95 | |||
96 | Adds a stub function with a signature matching the function under the cursor. | ||
97 | |||
98 | .Before | ||
99 | ```rust | ||
100 | struct Baz; | ||
101 | fn baz() -> Baz { Baz } | ||
102 | fn foo() { | ||
103 | bar┃("", baz()); | ||
104 | } | ||
105 | |||
106 | ``` | ||
107 | |||
108 | .After | ||
109 | ```rust | ||
110 | struct Baz; | ||
111 | fn baz() -> Baz { Baz } | ||
112 | fn foo() { | ||
113 | bar("", baz()); | ||
114 | } | ||
115 | |||
116 | fn bar(arg: &str, baz: Baz) { | ||
117 | ${0:todo!()} | ||
118 | } | ||
119 | |||
120 | ``` | ||
121 | |||
122 | |||
123 | [discrete] | ||
124 | === `add_hash` | ||
125 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L65[raw_string.rs] | ||
126 | |||
127 | Adds a hash to a raw string literal. | ||
128 | |||
129 | .Before | ||
130 | ```rust | ||
131 | fn main() { | ||
132 | r#"Hello,┃ World!"#; | ||
133 | } | ||
134 | ``` | ||
135 | |||
136 | .After | ||
137 | ```rust | ||
138 | fn main() { | ||
139 | r##"Hello, World!"##; | ||
140 | } | ||
141 | ``` | ||
142 | |||
143 | |||
144 | [discrete] | ||
145 | === `add_impl` | ||
146 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_impl.rs#L6[add_impl.rs] | ||
147 | |||
148 | Adds a new inherent impl for a type. | ||
149 | |||
150 | .Before | ||
151 | ```rust | ||
152 | struct Ctx<T: Clone> { | ||
153 | data: T,┃ | ||
154 | } | ||
155 | ``` | ||
156 | |||
157 | .After | ||
158 | ```rust | ||
159 | struct Ctx<T: Clone> { | ||
160 | data: T, | ||
161 | } | ||
162 | |||
163 | impl<T: Clone> Ctx<T> { | ||
164 | $0 | ||
165 | } | ||
166 | ``` | ||
167 | |||
168 | |||
169 | [discrete] | ||
170 | === `add_impl_default_members` | ||
171 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L64[add_missing_impl_members.rs] | ||
172 | |||
173 | Adds scaffold for overriding default impl members. | ||
174 | |||
175 | .Before | ||
176 | ```rust | ||
177 | trait Trait { | ||
178 | Type X; | ||
179 | fn foo(&self); | ||
180 | fn bar(&self) {} | ||
181 | } | ||
182 | |||
183 | impl Trait for () { | ||
184 | Type X = (); | ||
185 | fn foo(&self) {}┃ | ||
186 | |||
187 | } | ||
188 | ``` | ||
189 | |||
190 | .After | ||
191 | ```rust | ||
192 | trait Trait { | ||
193 | Type X; | ||
194 | fn foo(&self); | ||
195 | fn bar(&self) {} | ||
196 | } | ||
197 | |||
198 | impl Trait for () { | ||
199 | Type X = (); | ||
200 | fn foo(&self) {} | ||
201 | $0fn bar(&self) {} | ||
202 | |||
203 | } | ||
204 | ``` | ||
205 | |||
206 | |||
207 | [discrete] | ||
208 | === `add_impl_missing_members` | ||
209 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_missing_impl_members.rs#L24[add_missing_impl_members.rs] | ||
210 | |||
211 | Adds scaffold for required impl members. | ||
212 | |||
213 | .Before | ||
214 | ```rust | ||
215 | trait Trait<T> { | ||
216 | Type X; | ||
217 | fn foo(&self) -> T; | ||
218 | fn bar(&self) {} | ||
219 | } | ||
220 | |||
221 | impl Trait<u32> for () {┃ | ||
222 | |||
223 | } | ||
224 | ``` | ||
225 | |||
226 | .After | ||
227 | ```rust | ||
228 | trait Trait<T> { | ||
229 | Type X; | ||
230 | fn foo(&self) -> T; | ||
231 | fn bar(&self) {} | ||
232 | } | ||
233 | |||
234 | impl Trait<u32> for () { | ||
235 | fn foo(&self) -> u32 { | ||
236 | ${0:todo!()} | ||
237 | } | ||
238 | |||
239 | } | ||
240 | ``` | ||
241 | |||
242 | |||
243 | [discrete] | ||
244 | === `add_new` | ||
245 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_new.rs#L12[add_new.rs] | ||
246 | |||
247 | Adds a new inherent impl for a type. | ||
248 | |||
249 | .Before | ||
250 | ```rust | ||
251 | struct Ctx<T: Clone> { | ||
252 | data: T,┃ | ||
253 | } | ||
254 | ``` | ||
255 | |||
256 | .After | ||
257 | ```rust | ||
258 | struct Ctx<T: Clone> { | ||
259 | data: T, | ||
260 | } | ||
261 | |||
262 | impl<T: Clone> Ctx<T> { | ||
263 | fn $0new(data: T) -> Self { Self { data } } | ||
264 | } | ||
265 | |||
266 | ``` | ||
267 | |||
268 | |||
269 | [discrete] | ||
270 | === `add_turbo_fish` | ||
271 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/add_turbo_fish.rs#L10[add_turbo_fish.rs] | ||
272 | |||
273 | Adds `::<_>` to a call of a generic method or function. | ||
274 | |||
275 | .Before | ||
276 | ```rust | ||
277 | fn make<T>() -> T { todo!() } | ||
278 | fn main() { | ||
279 | let x = make┃(); | ||
280 | } | ||
281 | ``` | ||
282 | |||
283 | .After | ||
284 | ```rust | ||
285 | fn make<T>() -> T { todo!() } | ||
286 | fn main() { | ||
287 | let x = make::<${0:_}>(); | ||
288 | } | ||
289 | ``` | ||
290 | |||
291 | |||
292 | [discrete] | ||
293 | === `apply_demorgan` | ||
294 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/apply_demorgan.rs#L5[apply_demorgan.rs] | ||
295 | |||
296 | Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). | ||
297 | This transforms expressions of the form `!l || !r` into `!(l && r)`. | ||
298 | This also works with `&&`. This assist can only be applied with the cursor | ||
299 | on either `||` or `&&`, with both operands being a negation of some kind. | ||
300 | This means something of the form `!x` or `x != y`. | ||
301 | |||
302 | .Before | ||
303 | ```rust | ||
304 | fn main() { | ||
305 | if x != 4 ||┃ !y {} | ||
306 | } | ||
307 | ``` | ||
308 | |||
309 | .After | ||
310 | ```rust | ||
311 | fn main() { | ||
312 | if !(x == 4 && y) {} | ||
313 | } | ||
314 | ``` | ||
315 | |||
316 | |||
317 | [discrete] | ||
318 | === `auto_import` | ||
319 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/auto_import.rs#L18[auto_import.rs] | ||
320 | |||
321 | If the name is unresolved, provides all possible imports for it. | ||
322 | |||
323 | .Before | ||
324 | ```rust | ||
325 | fn main() { | ||
326 | let map = HashMap┃::new(); | ||
327 | } | ||
328 | ``` | ||
329 | |||
330 | .After | ||
331 | ```rust | ||
332 | use std::collections::HashMap; | ||
333 | |||
334 | fn main() { | ||
335 | let map = HashMap::new(); | ||
336 | } | ||
337 | ``` | ||
338 | |||
339 | |||
340 | [discrete] | ||
341 | === `change_return_type_to_result` | ||
342 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_return_type_to_result.rs#L8[change_return_type_to_result.rs] | ||
343 | |||
344 | Change the function's return type to Result. | ||
345 | |||
346 | .Before | ||
347 | ```rust | ||
348 | fn foo() -> i32┃ { 42i32 } | ||
349 | ``` | ||
350 | |||
351 | .After | ||
352 | ```rust | ||
353 | fn foo() -> Result<i32, ${0:_}> { Ok(42i32) } | ||
354 | ``` | ||
355 | |||
356 | |||
357 | [discrete] | ||
358 | === `change_visibility` | ||
359 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/change_visibility.rs#L14[change_visibility.rs] | ||
360 | |||
361 | Adds or changes existing visibility specifier. | ||
362 | |||
363 | .Before | ||
364 | ```rust | ||
365 | ┃fn frobnicate() {} | ||
366 | ``` | ||
367 | |||
368 | .After | ||
369 | ```rust | ||
370 | pub(crate) fn frobnicate() {} | ||
371 | ``` | ||
372 | |||
373 | |||
374 | [discrete] | ||
375 | === `convert_to_guarded_return` | ||
376 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/early_return.rs#L21[early_return.rs] | ||
377 | |||
378 | Replace a large conditional with a guarded return. | ||
379 | |||
380 | .Before | ||
381 | ```rust | ||
382 | fn main() { | ||
383 | ┃if cond { | ||
384 | foo(); | ||
385 | bar(); | ||
386 | } | ||
387 | } | ||
388 | ``` | ||
389 | |||
390 | .After | ||
391 | ```rust | ||
392 | fn main() { | ||
393 | if !cond { | ||
394 | return; | ||
395 | } | ||
396 | foo(); | ||
397 | bar(); | ||
398 | } | ||
399 | ``` | ||
400 | |||
401 | |||
402 | [discrete] | ||
403 | === `fill_match_arms` | ||
404 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fill_match_arms.rs#L14[fill_match_arms.rs] | ||
405 | |||
406 | Adds missing clauses to a `match` expression. | ||
407 | |||
408 | .Before | ||
409 | ```rust | ||
410 | enum Action { Move { distance: u32 }, Stop } | ||
411 | |||
412 | fn handle(action: Action) { | ||
413 | match action { | ||
414 | ┃ | ||
415 | } | ||
416 | } | ||
417 | ``` | ||
418 | |||
419 | .After | ||
420 | ```rust | ||
421 | enum Action { Move { distance: u32 }, Stop } | ||
422 | |||
423 | fn handle(action: Action) { | ||
424 | match action { | ||
425 | $0Action::Move { distance } => {} | ||
426 | Action::Stop => {} | ||
427 | } | ||
428 | } | ||
429 | ``` | ||
430 | |||
431 | |||
432 | [discrete] | ||
433 | === `fix_visibility` | ||
434 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/fix_visibility.rs#L13[fix_visibility.rs] | ||
435 | |||
436 | Makes inaccessible item public. | ||
437 | |||
438 | .Before | ||
439 | ```rust | ||
440 | mod m { | ||
441 | fn frobnicate() {} | ||
442 | } | ||
443 | fn main() { | ||
444 | m::frobnicate┃() {} | ||
445 | } | ||
446 | ``` | ||
447 | |||
448 | .After | ||
449 | ```rust | ||
450 | mod m { | ||
451 | $0pub(crate) fn frobnicate() {} | ||
452 | } | ||
453 | fn main() { | ||
454 | m::frobnicate() {} | ||
455 | } | ||
456 | ``` | ||
457 | |||
458 | |||
459 | [discrete] | ||
460 | === `flip_binexpr` | ||
461 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_binexpr.rs#L5[flip_binexpr.rs] | ||
462 | |||
463 | Flips operands of a binary expression. | ||
464 | |||
465 | .Before | ||
466 | ```rust | ||
467 | fn main() { | ||
468 | let _ = 90 +┃ 2; | ||
469 | } | ||
470 | ``` | ||
471 | |||
472 | .After | ||
473 | ```rust | ||
474 | fn main() { | ||
475 | let _ = 2 + 90; | ||
476 | } | ||
477 | ``` | ||
478 | |||
479 | |||
480 | [discrete] | ||
481 | === `flip_comma` | ||
482 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_comma.rs#L5[flip_comma.rs] | ||
483 | |||
484 | Flips two comma-separated items. | ||
485 | |||
486 | .Before | ||
487 | ```rust | ||
488 | fn main() { | ||
489 | ((1, 2),┃ (3, 4)); | ||
490 | } | ||
491 | ``` | ||
492 | |||
493 | .After | ||
494 | ```rust | ||
495 | fn main() { | ||
496 | ((3, 4), (1, 2)); | ||
497 | } | ||
498 | ``` | ||
499 | |||
500 | |||
501 | [discrete] | ||
502 | === `flip_trait_bound` | ||
503 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/flip_trait_bound.rs#L9[flip_trait_bound.rs] | ||
504 | |||
505 | Flips two trait bounds. | ||
506 | |||
507 | .Before | ||
508 | ```rust | ||
509 | fn foo<T: Clone +┃ Copy>() { } | ||
510 | ``` | ||
511 | |||
512 | .After | ||
513 | ```rust | ||
514 | fn foo<T: Copy + Clone>() { } | ||
515 | ``` | ||
516 | |||
517 | |||
518 | [discrete] | ||
519 | === `inline_local_variable` | ||
520 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/inline_local_variable.rs#L13[inline_local_variable.rs] | ||
521 | |||
522 | Inlines local variable. | ||
523 | |||
524 | .Before | ||
525 | ```rust | ||
526 | fn main() { | ||
527 | let x┃ = 1 + 2; | ||
528 | x * 4; | ||
529 | } | ||
530 | ``` | ||
531 | |||
532 | .After | ||
533 | ```rust | ||
534 | fn main() { | ||
535 | (1 + 2) * 4; | ||
536 | } | ||
537 | ``` | ||
538 | |||
539 | |||
540 | [discrete] | ||
541 | === `introduce_named_lifetime` | ||
542 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/introduce_named_lifetime.rs#L12[introduce_named_lifetime.rs] | ||
543 | |||
544 | Change an anonymous lifetime to a named lifetime. | ||
545 | |||
546 | .Before | ||
547 | ```rust | ||
548 | impl Cursor<'_┃> { | ||
549 | fn node(self) -> &SyntaxNode { | ||
550 | match self { | ||
551 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
552 | } | ||
553 | } | ||
554 | } | ||
555 | ``` | ||
556 | |||
557 | .After | ||
558 | ```rust | ||
559 | impl<'a> Cursor<'a> { | ||
560 | fn node(self) -> &SyntaxNode { | ||
561 | match self { | ||
562 | Cursor::Replace(node) | Cursor::Before(node) => node, | ||
563 | } | ||
564 | } | ||
565 | } | ||
566 | ``` | ||
567 | |||
568 | |||
569 | [discrete] | ||
570 | === `introduce_variable` | ||
571 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/introduce_variable.rs#L14[introduce_variable.rs] | ||
572 | |||
573 | Extracts subexpression into a variable. | ||
574 | |||
575 | .Before | ||
576 | ```rust | ||
577 | fn main() { | ||
578 | ┃(1 + 2)┃ * 4; | ||
579 | } | ||
580 | ``` | ||
581 | |||
582 | .After | ||
583 | ```rust | ||
584 | fn main() { | ||
585 | let $0var_name = (1 + 2); | ||
586 | var_name * 4; | ||
587 | } | ||
588 | ``` | ||
589 | |||
590 | |||
591 | [discrete] | ||
592 | === `invert_if` | ||
593 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/invert_if.rs#L12[invert_if.rs] | ||
594 | |||
595 | Apply invert_if | ||
596 | This transforms if expressions of the form `if !x {A} else {B}` into `if x {B} else {A}` | ||
597 | This also works with `!=`. This assist can only be applied with the cursor | ||
598 | on `if`. | ||
599 | |||
600 | .Before | ||
601 | ```rust | ||
602 | fn main() { | ||
603 | if┃ !y { A } else { B } | ||
604 | } | ||
605 | ``` | ||
606 | |||
607 | .After | ||
608 | ```rust | ||
609 | fn main() { | ||
610 | if y { B } else { A } | ||
611 | } | ||
612 | ``` | ||
613 | |||
614 | |||
615 | [discrete] | ||
616 | === `make_raw_string` | ||
617 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L10[raw_string.rs] | ||
618 | |||
619 | Adds `r#` to a plain string literal. | ||
620 | |||
621 | .Before | ||
622 | ```rust | ||
623 | fn main() { | ||
624 | "Hello,┃ World!"; | ||
625 | } | ||
626 | ``` | ||
627 | |||
628 | .After | ||
629 | ```rust | ||
630 | fn main() { | ||
631 | r#"Hello, World!"#; | ||
632 | } | ||
633 | ``` | ||
634 | |||
635 | |||
636 | [discrete] | ||
637 | === `make_usual_string` | ||
638 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L39[raw_string.rs] | ||
639 | |||
640 | Turns a raw string into a plain string. | ||
641 | |||
642 | .Before | ||
643 | ```rust | ||
644 | fn main() { | ||
645 | r#"Hello,┃ "World!""#; | ||
646 | } | ||
647 | ``` | ||
648 | |||
649 | .After | ||
650 | ```rust | ||
651 | fn main() { | ||
652 | "Hello, \"World!\""; | ||
653 | } | ||
654 | ``` | ||
655 | |||
656 | |||
657 | [discrete] | ||
658 | === `merge_imports` | ||
659 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_imports.rs#L14[merge_imports.rs] | ||
660 | |||
661 | Merges two imports with a common prefix. | ||
662 | |||
663 | .Before | ||
664 | ```rust | ||
665 | use std::┃fmt::Formatter; | ||
666 | use std::io; | ||
667 | ``` | ||
668 | |||
669 | .After | ||
670 | ```rust | ||
671 | use std::{fmt::Formatter, io}; | ||
672 | ``` | ||
673 | |||
674 | |||
675 | [discrete] | ||
676 | === `merge_match_arms` | ||
677 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/merge_match_arms.rs#L11[merge_match_arms.rs] | ||
678 | |||
679 | Merges identical match arms. | ||
680 | |||
681 | .Before | ||
682 | ```rust | ||
683 | enum Action { Move { distance: u32 }, Stop } | ||
684 | |||
685 | fn handle(action: Action) { | ||
686 | match action { | ||
687 | ┃Action::Move(..) => foo(), | ||
688 | Action::Stop => foo(), | ||
689 | } | ||
690 | } | ||
691 | ``` | ||
692 | |||
693 | .After | ||
694 | ```rust | ||
695 | enum Action { Move { distance: u32 }, Stop } | ||
696 | |||
697 | fn handle(action: Action) { | ||
698 | match action { | ||
699 | Action::Move(..) | Action::Stop => foo(), | ||
700 | } | ||
701 | } | ||
702 | ``` | ||
703 | |||
704 | |||
705 | [discrete] | ||
706 | === `move_arm_cond_to_match_guard` | ||
707 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L56[move_guard.rs] | ||
708 | |||
709 | Moves if expression from match arm body into a guard. | ||
710 | |||
711 | .Before | ||
712 | ```rust | ||
713 | enum Action { Move { distance: u32 }, Stop } | ||
714 | |||
715 | fn handle(action: Action) { | ||
716 | match action { | ||
717 | Action::Move { distance } => ┃if distance > 10 { foo() }, | ||
718 | _ => (), | ||
719 | } | ||
720 | } | ||
721 | ``` | ||
722 | |||
723 | .After | ||
724 | ```rust | ||
725 | enum Action { Move { distance: u32 }, Stop } | ||
726 | |||
727 | fn handle(action: Action) { | ||
728 | match action { | ||
729 | Action::Move { distance } if distance > 10 => foo(), | ||
730 | _ => (), | ||
731 | } | ||
732 | } | ||
733 | ``` | ||
734 | |||
735 | |||
736 | [discrete] | ||
737 | === `move_bounds_to_where_clause` | ||
738 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_bounds.rs#L10[move_bounds.rs] | ||
739 | |||
740 | Moves inline type bounds to a where clause. | ||
741 | |||
742 | .Before | ||
743 | ```rust | ||
744 | fn apply<T, U, ┃F: FnOnce(T) -> U>(f: F, x: T) -> U { | ||
745 | f(x) | ||
746 | } | ||
747 | ``` | ||
748 | |||
749 | .After | ||
750 | ```rust | ||
751 | fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U { | ||
752 | f(x) | ||
753 | } | ||
754 | ``` | ||
755 | |||
756 | |||
757 | [discrete] | ||
758 | === `move_guard_to_arm_body` | ||
759 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/move_guard.rs#L8[move_guard.rs] | ||
760 | |||
761 | Moves match guard into match arm body. | ||
762 | |||
763 | .Before | ||
764 | ```rust | ||
765 | enum Action { Move { distance: u32 }, Stop } | ||
766 | |||
767 | fn handle(action: Action) { | ||
768 | match action { | ||
769 | Action::Move { distance } ┃if distance > 10 => foo(), | ||
770 | _ => (), | ||
771 | } | ||
772 | } | ||
773 | ``` | ||
774 | |||
775 | .After | ||
776 | ```rust | ||
777 | enum Action { Move { distance: u32 }, Stop } | ||
778 | |||
779 | fn handle(action: Action) { | ||
780 | match action { | ||
781 | Action::Move { distance } => if distance > 10 { foo() }, | ||
782 | _ => (), | ||
783 | } | ||
784 | } | ||
785 | ``` | ||
786 | |||
787 | |||
788 | [discrete] | ||
789 | === `remove_dbg` | ||
790 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_dbg.rs#L8[remove_dbg.rs] | ||
791 | |||
792 | Removes `dbg!()` macro call. | ||
793 | |||
794 | .Before | ||
795 | ```rust | ||
796 | fn main() { | ||
797 | ┃dbg!(92); | ||
798 | } | ||
799 | ``` | ||
800 | |||
801 | .After | ||
802 | ```rust | ||
803 | fn main() { | ||
804 | 92; | ||
805 | } | ||
806 | ``` | ||
807 | |||
808 | |||
809 | [discrete] | ||
810 | === `remove_hash` | ||
811 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/raw_string.rs#L89[raw_string.rs] | ||
812 | |||
813 | Removes a hash from a raw string literal. | ||
814 | |||
815 | .Before | ||
816 | ```rust | ||
817 | fn main() { | ||
818 | r#"Hello,┃ World!"#; | ||
819 | } | ||
820 | ``` | ||
821 | |||
822 | .After | ||
823 | ```rust | ||
824 | fn main() { | ||
825 | r"Hello, World!"; | ||
826 | } | ||
827 | ``` | ||
828 | |||
829 | |||
830 | [discrete] | ||
831 | === `remove_mut` | ||
832 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/remove_mut.rs#L5[remove_mut.rs] | ||
833 | |||
834 | Removes the `mut` keyword. | ||
835 | |||
836 | .Before | ||
837 | ```rust | ||
838 | impl Walrus { | ||
839 | fn feed(&mut┃ self, amount: u32) {} | ||
840 | } | ||
841 | ``` | ||
842 | |||
843 | .After | ||
844 | ```rust | ||
845 | impl Walrus { | ||
846 | fn feed(&self, amount: u32) {} | ||
847 | } | ||
848 | ``` | ||
849 | |||
850 | |||
851 | [discrete] | ||
852 | === `reorder_fields` | ||
853 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/reorder_fields.rs#L10[reorder_fields.rs] | ||
854 | |||
855 | Reorder the fields of record literals and record patterns in the same order as in | ||
856 | the definition. | ||
857 | |||
858 | .Before | ||
859 | ```rust | ||
860 | struct Foo {foo: i32, bar: i32}; | ||
861 | const test: Foo = ┃Foo {bar: 0, foo: 1} | ||
862 | ``` | ||
863 | |||
864 | .After | ||
865 | ```rust | ||
866 | struct Foo {foo: i32, bar: i32}; | ||
867 | const test: Foo = Foo {foo: 1, bar: 0} | ||
868 | ``` | ||
869 | |||
870 | |||
871 | [discrete] | ||
872 | === `replace_if_let_with_match` | ||
873 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_if_let_with_match.rs#L13[replace_if_let_with_match.rs] | ||
874 | |||
875 | Replaces `if let` with an else branch with a `match` expression. | ||
876 | |||
877 | .Before | ||
878 | ```rust | ||
879 | enum Action { Move { distance: u32 }, Stop } | ||
880 | |||
881 | fn handle(action: Action) { | ||
882 | ┃if let Action::Move { distance } = action { | ||
883 | foo(distance) | ||
884 | } else { | ||
885 | bar() | ||
886 | } | ||
887 | } | ||
888 | ``` | ||
889 | |||
890 | .After | ||
891 | ```rust | ||
892 | enum Action { Move { distance: u32 }, Stop } | ||
893 | |||
894 | fn handle(action: Action) { | ||
895 | match action { | ||
896 | Action::Move { distance } => foo(distance), | ||
897 | _ => bar(), | ||
898 | } | ||
899 | } | ||
900 | ``` | ||
901 | |||
902 | |||
903 | [discrete] | ||
904 | === `replace_let_with_if_let` | ||
905 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_let_with_if_let.rs#L14[replace_let_with_if_let.rs] | ||
906 | |||
907 | Replaces `let` with an `if-let`. | ||
908 | |||
909 | .Before | ||
910 | ```rust | ||
911 | |||
912 | fn main(action: Action) { | ||
913 | ┃let x = compute(); | ||
914 | } | ||
915 | |||
916 | fn compute() -> Option<i32> { None } | ||
917 | ``` | ||
918 | |||
919 | .After | ||
920 | ```rust | ||
921 | |||
922 | fn main(action: Action) { | ||
923 | if let Some(x) = compute() { | ||
924 | } | ||
925 | } | ||
926 | |||
927 | fn compute() -> Option<i32> { None } | ||
928 | ``` | ||
929 | |||
930 | |||
931 | [discrete] | ||
932 | === `replace_qualified_name_with_use` | ||
933 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs#L6[replace_qualified_name_with_use.rs] | ||
934 | |||
935 | Adds a use statement for a given fully-qualified name. | ||
936 | |||
937 | .Before | ||
938 | ```rust | ||
939 | fn process(map: std::collections::┃HashMap<String, String>) {} | ||
940 | ``` | ||
941 | |||
942 | .After | ||
943 | ```rust | ||
944 | use std::collections::HashMap; | ||
945 | |||
946 | fn process(map: HashMap<String, String>) {} | ||
947 | ``` | ||
948 | |||
949 | |||
950 | [discrete] | ||
951 | === `replace_unwrap_with_match` | ||
952 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs#L17[replace_unwrap_with_match.rs] | ||
953 | |||
954 | Replaces `unwrap` a `match` expression. Works for Result and Option. | ||
955 | |||
956 | .Before | ||
957 | ```rust | ||
958 | enum Result<T, E> { Ok(T), Err(E) } | ||
959 | fn main() { | ||
960 | let x: Result<i32, i32> = Result::Ok(92); | ||
961 | let y = x.┃unwrap(); | ||
962 | } | ||
963 | ``` | ||
964 | |||
965 | .After | ||
966 | ```rust | ||
967 | enum Result<T, E> { Ok(T), Err(E) } | ||
968 | fn main() { | ||
969 | let x: Result<i32, i32> = Result::Ok(92); | ||
970 | let y = match x { | ||
971 | Ok(a) => a, | ||
972 | $0_ => unreachable!(), | ||
973 | }; | ||
974 | } | ||
975 | ``` | ||
976 | |||
977 | |||
978 | [discrete] | ||
979 | === `split_import` | ||
980 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/split_import.rs#L7[split_import.rs] | ||
981 | |||
982 | Wraps the tail of import into braces. | ||
983 | |||
984 | .Before | ||
985 | ```rust | ||
986 | use std::┃collections::HashMap; | ||
987 | ``` | ||
988 | |||
989 | .After | ||
990 | ```rust | ||
991 | use std::{collections::HashMap}; | ||
992 | ``` | ||
993 | |||
994 | |||
995 | [discrete] | ||
996 | === `unwrap_block` | ||
997 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_assists/src/handlers/unwrap_block.rs#L9[unwrap_block.rs] | ||
998 | |||
999 | This assist removes if...else, for, while and loop control statements to just keep the body. | ||
1000 | |||
1001 | .Before | ||
1002 | ```rust | ||
1003 | fn foo() { | ||
1004 | if true {┃ | ||
1005 | println!("foo"); | ||
1006 | } | ||
1007 | } | ||
1008 | ``` | ||
1009 | |||
1010 | .After | ||
1011 | ```rust | ||
1012 | fn foo() { | ||
1013 | println!("foo"); | ||
1014 | } | ||
1015 | ``` | ||
diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc deleted file mode 100644 index 12812fa0b..000000000 --- a/docs/user/generated_features.adoc +++ /dev/null | |||
@@ -1,298 +0,0 @@ | |||
1 | === Expand Macro Recursively | ||
2 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs#L15[expand_macro.rs] | ||
3 | |||
4 | Shows the full macro expansion of the macro at current cursor. | ||
5 | |||
6 | |=== | ||
7 | | Editor | Action Name | ||
8 | |||
9 | | VS Code | **Rust Analyzer: Expand macro recursively** | ||
10 | |=== | ||
11 | |||
12 | |||
13 | === Extend Selection | ||
14 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs#L15[extend_selection.rs] | ||
15 | |||
16 | Extends the current selection to the encompassing syntactic construct | ||
17 | (expression, statement, item, module, etc). It works with multiple cursors. | ||
18 | |||
19 | |=== | ||
20 | | Editor | Shortcut | ||
21 | |||
22 | | VS Code | kbd:[Ctrl+Shift+→] | ||
23 | |=== | ||
24 | |||
25 | |||
26 | === File Structure | ||
27 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs#L17[structure.rs] | ||
28 | |||
29 | Provides a tree of the symbols defined in the file. Can be used to | ||
30 | |||
31 | * fuzzy search symbol in a file (super useful) | ||
32 | * draw breadcrumbs to describe the context around the cursor | ||
33 | * draw outline of the file | ||
34 | |||
35 | |=== | ||
36 | | Editor | Shortcut | ||
37 | |||
38 | | VS Code | kbd:[Ctrl+Shift+O] | ||
39 | |=== | ||
40 | |||
41 | |||
42 | === Go to Definition | ||
43 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs#L18[goto_definition.rs] | ||
44 | |||
45 | Navigates to the definition of an identifier. | ||
46 | |||
47 | |=== | ||
48 | | Editor | Shortcut | ||
49 | |||
50 | | VS Code | kbd:[F12] | ||
51 | |=== | ||
52 | |||
53 | |||
54 | === Go to Implementation | ||
55 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs#L7[goto_implementation.rs] | ||
56 | |||
57 | Navigates to the impl block of structs, enums or traits. Also implemented as a code lens. | ||
58 | |||
59 | |=== | ||
60 | | Editor | Shortcut | ||
61 | |||
62 | | VS Code | kbd:[Ctrl+F12] | ||
63 | |=== | ||
64 | |||
65 | |||
66 | === Go to Type Definition | ||
67 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs#L6[goto_type_definition.rs] | ||
68 | |||
69 | Navigates to the type of an identifier. | ||
70 | |||
71 | |=== | ||
72 | | Editor | Action Name | ||
73 | |||
74 | | VS Code | **Go to Type Definition* | ||
75 | |=== | ||
76 | |||
77 | |||
78 | === Hover | ||
79 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs#L63[hover.rs] | ||
80 | |||
81 | Shows additional information, like type of an expression or documentation for definition when "focusing" code. | ||
82 | Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. | ||
83 | |||
84 | |||
85 | === Inlay Hints | ||
86 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs#L40[inlay_hints.rs] | ||
87 | |||
88 | rust-analyzer shows additional information inline with the source code. | ||
89 | Editors usually render this using read-only virtual text snippets interspersed with code. | ||
90 | |||
91 | rust-analyzer shows hits for | ||
92 | |||
93 | * types of local variables | ||
94 | * names of function arguments | ||
95 | * types of chained expressions | ||
96 | |||
97 | **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations. | ||
98 | This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird: | ||
99 | https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2]. | ||
100 | |||
101 | |=== | ||
102 | | Editor | Action Name | ||
103 | |||
104 | | VS Code | **Rust Analyzer: Toggle inlay hints* | ||
105 | |=== | ||
106 | |||
107 | |||
108 | === Join Lines | ||
109 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs#L12[join_lines.rs] | ||
110 | |||
111 | Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. | ||
112 | |||
113 | |=== | ||
114 | | Editor | Action Name | ||
115 | |||
116 | | VS Code | **Rust Analyzer: Join lines** | ||
117 | |=== | ||
118 | |||
119 | |||
120 | === Magic Completions | ||
121 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs#L38[completion.rs] | ||
122 | |||
123 | In addition to usual reference completion, rust-analyzer provides some ✨magic✨ | ||
124 | completions as well: | ||
125 | |||
126 | Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor | ||
127 | is placed at the appropriate position. Even though `if` is easy to type, you | ||
128 | still want to complete it, to get ` { }` for free! `return` is inserted with a | ||
129 | space or `;` depending on the return type of the function. | ||
130 | |||
131 | When completing a function call, `()` are automatically inserted. If a function | ||
132 | takes arguments, the cursor is positioned inside the parenthesis. | ||
133 | |||
134 | There are postfix completions, which can be triggered by typing something like | ||
135 | `foo().if`. The word after `.` determines postfix completion. Possible variants are: | ||
136 | |||
137 | - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result` | ||
138 | - `expr.match` -> `match expr {}` | ||
139 | - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result` | ||
140 | - `expr.ref` -> `&expr` | ||
141 | - `expr.refm` -> `&mut expr` | ||
142 | - `expr.not` -> `!expr` | ||
143 | - `expr.dbg` -> `dbg!(expr)` | ||
144 | |||
145 | There also snippet completions: | ||
146 | |||
147 | .Expressions | ||
148 | - `pd` -> `println!("{:?}")` | ||
149 | - `ppd` -> `println!("{:#?}")` | ||
150 | |||
151 | .Items | ||
152 | - `tfn` -> `#[test] fn f(){}` | ||
153 | - `tmod` -> | ||
154 | ```rust | ||
155 | #[cfg(test)] | ||
156 | mod tests { | ||
157 | use super::*; | ||
158 | |||
159 | #[test] | ||
160 | fn test_fn() {} | ||
161 | } | ||
162 | ``` | ||
163 | |||
164 | |||
165 | === Matching Brace | ||
166 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs#L3[matching_brace.rs] | ||
167 | |||
168 | If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair, | ||
169 | moves cursor to the matching brace. It uses the actual parser to determine | ||
170 | braces, so it won't confuse generics with comparisons. | ||
171 | |||
172 | |=== | ||
173 | | Editor | Action Name | ||
174 | |||
175 | | VS Code | **Rust Analyzer: Find matching brace** | ||
176 | |=== | ||
177 | |||
178 | |||
179 | === On Typing Assists | ||
180 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs#L35[typing.rs] | ||
181 | |||
182 | Some features trigger on typing certain characters: | ||
183 | |||
184 | - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression | ||
185 | - Enter inside comments automatically inserts `///` | ||
186 | - typing `.` in a chain method call auto-indents | ||
187 | |||
188 | |||
189 | === Parent Module | ||
190 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs#L12[parent_module.rs] | ||
191 | |||
192 | Navigates to the parent module of the current module. | ||
193 | |||
194 | |=== | ||
195 | | Editor | Action Name | ||
196 | |||
197 | | VS Code | **Rust Analyzer: Locate parent module** | ||
198 | |=== | ||
199 | |||
200 | |||
201 | === Run | ||
202 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs#L45[runnables.rs] | ||
203 | |||
204 | Shows a popup suggesting to run a test/benchmark/binary **at the current cursor | ||
205 | location**. Super useful for repeatedly running just a single test. Do bind this | ||
206 | to a shortcut! | ||
207 | |||
208 | |=== | ||
209 | | Editor | Action Name | ||
210 | |||
211 | | VS Code | **Rust Analyzer: Run** | ||
212 | |=== | ||
213 | |||
214 | |||
215 | === Semantic Syntax Highlighting | ||
216 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs#L33[syntax_highlighting.rs] | ||
217 | |||
218 | rust-analyzer highlights the code semantically. | ||
219 | For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait. | ||
220 | rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token. | ||
221 | It's up to the client to map those to specific colors. | ||
222 | |||
223 | The general rule is that a reference to an entity gets colored the same way as the entity itself. | ||
224 | We also give special modifier for `mut` and `&mut` local variables. | ||
225 | |||
226 | |||
227 | === Show Syntax Tree | ||
228 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs#L9[syntax_tree.rs] | ||
229 | |||
230 | Shows the parse tree of the current file. It exists mostly for debugging | ||
231 | rust-analyzer itself. | ||
232 | |||
233 | |=== | ||
234 | | Editor | Action Name | ||
235 | |||
236 | | VS Code | **Rust Analyzer: Show Syntax Tree** | ||
237 | |=== | ||
238 | |||
239 | |||
240 | === Status | ||
241 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs#L27[status.rs] | ||
242 | |||
243 | Shows internal statistic about memory usage of rust-analyzer. | ||
244 | |||
245 | |=== | ||
246 | | Editor | Action Name | ||
247 | |||
248 | | VS Code | **Rust Analyzer: Status** | ||
249 | |=== | ||
250 | |||
251 | |||
252 | === Structural Seach and Replace | ||
253 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs#L26[ssr.rs] | ||
254 | |||
255 | Search and replace with named wildcards that will match any expression. | ||
256 | The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. | ||
257 | A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. | ||
258 | Available via the command `rust-analyzer.ssr`. | ||
259 | |||
260 | ```rust | ||
261 | // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)] | ||
262 | |||
263 | // BEFORE | ||
264 | String::from(foo(y + 5, z)) | ||
265 | |||
266 | // AFTER | ||
267 | String::from((y + 5).foo(z)) | ||
268 | ``` | ||
269 | |||
270 | |=== | ||
271 | | Editor | Action Name | ||
272 | |||
273 | | VS Code | **Rust Analyzer: Structural Search Replace** | ||
274 | |=== | ||
275 | |||
276 | |||
277 | === Workspace Symbol | ||
278 | **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs#L113[symbol_index.rs] | ||
279 | |||
280 | Uses fuzzy-search to find types, modules and functions by name across your | ||
281 | project and dependencies. This is **the** most useful feature, which improves code | ||
282 | navigation tremendously. It mostly works on top of the built-in LSP | ||
283 | functionality, however `#` and `*` symbols can be used to narrow down the | ||
284 | search. Specifically, | ||
285 | |||
286 | - `Foo` searches for `Foo` type in the current workspace | ||
287 | - `foo#` searches for `foo` function in the current workspace | ||
288 | - `Foo*` searches for `Foo` type among dependencies, including `stdlib` | ||
289 | - `foo#*` searches for `foo` function among dependencies | ||
290 | |||
291 | That is, `#` switches from "types" to all symbols, `*` switches from the current | ||
292 | workspace to dependencies. | ||
293 | |||
294 | |=== | ||
295 | | Editor | Shortcut | ||
296 | |||
297 | | VS Code | kbd:[Ctrl+T] | ||
298 | |=== | ||
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 202783fd9..ea714f49a 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -269,6 +269,57 @@ Gnome Builder currently has support for RLS, and there's no way to configure the | |||
269 | 1. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`). | 269 | 1. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`). |
270 | 2. Enable the Rust Builder plugin. | 270 | 2. Enable the Rust Builder plugin. |
271 | 271 | ||
272 | == Non-Cargo Based Projects | ||
273 | |||
274 | rust-analyzer does not require Cargo. | ||
275 | However, if you use some other build system, you'll have to describe the structure of your project for rust-analyzer in the `rust-project.json` format: | ||
276 | |||
277 | [source,TypeScript] | ||
278 | ---- | ||
279 | interface JsonProject { | ||
280 | /// The set of paths containing the crates for this project. | ||
281 | /// Any `Crate` must be nested inside some `root`. | ||
282 | roots: string[]; | ||
283 | /// The set of crates comprising the current project. | ||
284 | /// Must include all transitive dependencies as well as sysroot crate (libstd, libcore and such). | ||
285 | crates: Crate[]; | ||
286 | } | ||
287 | |||
288 | interface Crate { | ||
289 | /// Path to the root module of the crate. | ||
290 | root_module: string; | ||
291 | /// Edition of the crate. | ||
292 | edition: "2015" | "2018"; | ||
293 | /// Dependencies | ||
294 | deps: Dep[]; | ||
295 | /// The set of cfgs activated for a given crate, like `["unix", "feature=foo", "feature=bar"]`. | ||
296 | cfg: string[]; | ||
297 | |||
298 | /// value of the OUT_DIR env variable. | ||
299 | out_dir?: string; | ||
300 | /// For proc-macro crates, path to compiles proc-macro (.so file). | ||
301 | proc_macro_dylib_path?: string; | ||
302 | } | ||
303 | |||
304 | interface Dep { | ||
305 | /// Index of a crate in the `crates` array. | ||
306 | crate: number, | ||
307 | /// Name as should appear in the (implicit) `extern crate name` declaration. | ||
308 | name: string, | ||
309 | } | ||
310 | ---- | ||
311 | |||
312 | This format is provisional and subject to change. | ||
313 | Specifically, the `roots` setup will be different eventually. | ||
314 | |||
315 | There are tree ways to feed `rust-project.json` to rust-analyzer: | ||
316 | |||
317 | * Place `rust-project.json` file at the root of the project, and rust-anlayzer will discover it. | ||
318 | * Specify `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in the settings (and make sure that your LSP client sends settings as a part of initialize request). | ||
319 | * Specify `"rust-analyzer.linkedProjects": [ { "roots": [...], "crates": [...] }]` inline. | ||
320 | |||
321 | See https://github.com/rust-analyzer/rust-project.json-example for a small example. | ||
322 | |||
272 | == Features | 323 | == Features |
273 | 324 | ||
274 | include::./generated_features.adoc[] | 325 | include::./generated_features.adoc[] |
diff --git a/editors/code/package.json b/editors/code/package.json index d8f4287fd..30ab7ba4a 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -475,6 +475,25 @@ | |||
475 | "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 475 | "markdownDescription": "Whether to show Implementations lens. Only applies when `#rust-analyzer.lens.enable#` is set.", |
476 | "type": "boolean", | 476 | "type": "boolean", |
477 | "default": true | 477 | "default": true |
478 | }, | ||
479 | "rust-analyzer.linkedProjects": { | ||
480 | "markdownDescription": [ | ||
481 | "Disable project auto-discovery in favor of explicitly specified set of projects.", | ||
482 | "Elements must be paths pointing to Cargo.toml, rust-project.json, or JSON objects in rust-project.json format" | ||
483 | ], | ||
484 | "type": "array", | ||
485 | "items": { | ||
486 | "type": [ | ||
487 | "string", | ||
488 | "object" | ||
489 | ] | ||
490 | }, | ||
491 | "default": null | ||
492 | }, | ||
493 | "rust-analyzer.withSysroot": { | ||
494 | "markdownDescription": "Internal config for debugging, disables loading of sysroot crates", | ||
495 | "type": "boolean", | ||
496 | "default": true | ||
478 | } | 497 | } |
479 | } | 498 | } |
480 | }, | 499 | }, |
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 5511c01d5..f5f4b964a 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs | |||
@@ -18,8 +18,10 @@ use std::{ | |||
18 | use crate::{not_bash::fs2, project_root, Result}; | 18 | use crate::{not_bash::fs2, project_root, Result}; |
19 | 19 | ||
20 | pub use self::{ | 20 | pub use self::{ |
21 | gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs, | 21 | gen_assists_docs::{generate_assists_docs, generate_assists_tests}, |
22 | gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax, | 22 | gen_feature_docs::generate_feature_docs, |
23 | gen_parser_tests::generate_parser_tests, | ||
24 | gen_syntax::generate_syntax, | ||
23 | }; | 25 | }; |
24 | 26 | ||
25 | const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar"; | 27 | const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar"; |
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index 6c1be5350..526941f73 100644 --- a/xtask/src/codegen/gen_assists_docs.rs +++ b/xtask/src/codegen/gen_assists_docs.rs | |||
@@ -7,16 +7,17 @@ use crate::{ | |||
7 | project_root, rust_files, Result, | 7 | project_root, rust_files, Result, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | pub fn generate_assists_docs(mode: Mode) -> Result<()> { | 10 | pub fn generate_assists_tests(mode: Mode) -> Result<()> { |
11 | let assists = Assist::collect()?; | 11 | let assists = Assist::collect()?; |
12 | generate_tests(&assists, mode)?; | 12 | generate_tests(&assists, mode) |
13 | } | ||
13 | 14 | ||
15 | pub fn generate_assists_docs(mode: Mode) -> Result<()> { | ||
16 | let assists = Assist::collect()?; | ||
14 | let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); | 17 | let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); |
15 | let contents = contents.trim().to_string() + "\n"; | 18 | let contents = contents.trim().to_string() + "\n"; |
16 | let dst = project_root().join("docs/user/generated_assists.adoc"); | 19 | let dst = project_root().join("docs/user/generated_assists.adoc"); |
17 | codegen::update(&dst, &contents, mode)?; | 20 | codegen::update(&dst, &contents, mode) |
18 | |||
19 | Ok(()) | ||
20 | } | 21 | } |
21 | 22 | ||
22 | #[derive(Debug)] | 23 | #[derive(Debug)] |
diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 874957885..739f49f7b 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs | |||
@@ -160,6 +160,8 @@ pub fn run_release(dry_run: bool) -> Result<()> { | |||
160 | run!("git reset --hard tags/nightly")?; | 160 | run!("git reset --hard tags/nightly")?; |
161 | run!("git push")?; | 161 | run!("git push")?; |
162 | } | 162 | } |
163 | codegen::generate_assists_docs(Mode::Overwrite)?; | ||
164 | codegen::generate_feature_docs(Mode::Overwrite)?; | ||
163 | 165 | ||
164 | let website_root = project_root().join("../rust-analyzer.github.io"); | 166 | let website_root = project_root().join("../rust-analyzer.github.io"); |
165 | let changelog_dir = website_root.join("./thisweek/_posts"); | 167 | let changelog_dir = website_root.join("./thisweek/_posts"); |
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 9d7cdd114..81bb3a33f 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -74,6 +74,7 @@ FLAGS: | |||
74 | args.finish()?; | 74 | args.finish()?; |
75 | codegen::generate_syntax(Mode::Overwrite)?; | 75 | codegen::generate_syntax(Mode::Overwrite)?; |
76 | codegen::generate_parser_tests(Mode::Overwrite)?; | 76 | codegen::generate_parser_tests(Mode::Overwrite)?; |
77 | codegen::generate_assists_tests(Mode::Overwrite)?; | ||
77 | codegen::generate_assists_docs(Mode::Overwrite)?; | 78 | codegen::generate_assists_docs(Mode::Overwrite)?; |
78 | codegen::generate_feature_docs(Mode::Overwrite)?; | 79 | codegen::generate_feature_docs(Mode::Overwrite)?; |
79 | Ok(()) | 80 | Ok(()) |
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index 4ac5d929f..d38ac7f17 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs | |||
@@ -25,19 +25,12 @@ fn generated_tests_are_fresh() { | |||
25 | 25 | ||
26 | #[test] | 26 | #[test] |
27 | fn generated_assists_are_fresh() { | 27 | fn generated_assists_are_fresh() { |
28 | if let Err(error) = codegen::generate_assists_docs(Mode::Verify) { | 28 | if let Err(error) = codegen::generate_assists_tests(Mode::Verify) { |
29 | panic!("{}. Please update assists by running `cargo xtask codegen`", error); | 29 | panic!("{}. Please update assists by running `cargo xtask codegen`", error); |
30 | } | 30 | } |
31 | } | 31 | } |
32 | 32 | ||
33 | #[test] | 33 | #[test] |
34 | fn generated_features_are_fresh() { | ||
35 | if let Err(error) = codegen::generate_feature_docs(Mode::Verify) { | ||
36 | panic!("{}. Please update features by running `cargo xtask codegen`", error); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | #[test] | ||
41 | fn check_code_formatting() { | 34 | fn check_code_formatting() { |
42 | if let Err(error) = run_rustfmt(Mode::Verify) { | 35 | if let Err(error) = run_rustfmt(Mode::Verify) { |
43 | panic!("{}. Please format the code by running `cargo format`", error); | 36 | panic!("{}. Please format the code by running `cargo format`", error); |
@@ -180,13 +173,11 @@ impl TidyDocs { | |||
180 | } | 173 | } |
181 | 174 | ||
182 | fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool { | 175 | fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool { |
183 | let mut cur_path = p; | 176 | p.strip_prefix(project_root()) |
184 | while let Some(path) = cur_path.parent() { | 177 | .unwrap() |
185 | if dirs_to_exclude.iter().any(|dir| path.ends_with(dir)) { | 178 | .components() |
186 | return true; | 179 | .rev() |
187 | } | 180 | .skip(1) |
188 | cur_path = path; | 181 | .filter_map(|it| it.as_os_str().to_str()) |
189 | } | 182 | .any(|it| dirs_to_exclude.contains(&it)) |
190 | |||
191 | false | ||
192 | } | 183 | } |