aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock28
-rw-r--r--crates/ra_db/src/fixture.rs53
-rw-r--r--crates/ra_db/src/input.rs10
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs28
-rw-r--r--crates/ra_ide/src/completion.rs25
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs31
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs144
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs5
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs10
-rw-r--r--crates/ra_ide/src/completion/presentation.rs16
-rw-r--r--crates/ra_ide/src/lib.rs26
-rw-r--r--crates/ra_ide_db/Cargo.toml2
-rw-r--r--crates/ra_ide_db/src/lib.rs10
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs4
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs12
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs4
-rw-r--r--crates/rust-analyzer/src/feature_flags.rs (renamed from crates/ra_ide_db/src/feature_flags.rs)4
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop.rs7
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs16
-rw-r--r--crates/rust-analyzer/src/world.rs18
-rw-r--r--crates/test_utils/src/lib.rs13
-rw-r--r--editors/code/package-lock.json6
-rw-r--r--editors/code/package.json4
25 files changed, 346 insertions, 133 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e3ad50623..330bdd1cb 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -88,21 +88,6 @@ dependencies = [
88] 88]
89 89
90[[package]] 90[[package]]
91name = "byteorder"
92version = "1.3.4"
93source = "registry+https://github.com/rust-lang/crates.io-index"
94checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
95
96[[package]]
97name = "c2-chacha"
98version = "0.2.3"
99source = "registry+https://github.com/rust-lang/crates.io-index"
100checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
101dependencies = [
102 "ppv-lite86",
103]
104
105[[package]]
106name = "cargo_metadata" 91name = "cargo_metadata"
107version = "0.9.1" 92version = "0.9.1"
108source = "registry+https://github.com/rust-lang/crates.io-index" 93source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -401,12 +386,9 @@ dependencies = [
401 386
402[[package]] 387[[package]]
403name = "fst" 388name = "fst"
404version = "0.3.5" 389version = "0.4.0"
405source = "registry+https://github.com/rust-lang/crates.io-index" 390source = "registry+https://github.com/rust-lang/crates.io-index"
406checksum = "927fb434ff9f0115b215dc0efd2e4fbdd7448522a92a1aa37c77d6a2f8f1ebd6" 391checksum = "3f7c13470d799474d44e2b9c6a0925807def7af4d120cd4de761433be76f7579"
407dependencies = [
408 "byteorder",
409]
410 392
411[[package]] 393[[package]]
412name = "fuchsia-zircon" 394name = "fuchsia-zircon"
@@ -1180,11 +1162,11 @@ dependencies = [
1180 1162
1181[[package]] 1163[[package]]
1182name = "rand_chacha" 1164name = "rand_chacha"
1183version = "0.2.1" 1165version = "0.2.2"
1184source = "registry+https://github.com/rust-lang/crates.io-index" 1166source = "registry+https://github.com/rust-lang/crates.io-index"
1185checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" 1167checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
1186dependencies = [ 1168dependencies = [
1187 "c2-chacha", 1169 "ppv-lite86",
1188 "rand_core", 1170 "rand_core",
1189] 1171]
1190 1172
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index 947d6ad56..7f43c2971 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -5,7 +5,7 @@ use std::sync::Arc;
5 5
6use ra_cfg::CfgOptions; 6use ra_cfg::CfgOptions;
7use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
8use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; 8use test_utils::{extract_offset, parse_fixture, parse_single_fixture, CURSOR_MARKER};
9 9
10use crate::{ 10use crate::{
11 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, 11 input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf,
@@ -45,23 +45,37 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
45 45
46impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} 46impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {}
47 47
48fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId { 48fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId {
49 let file_id = FileId(0); 49 let file_id = FileId(0);
50 let rel_path: RelativePathBuf = "/main.rs".into(); 50 let rel_path: RelativePathBuf = "/main.rs".into();
51 51
52 let mut source_root = SourceRoot::new_local(); 52 let mut source_root = SourceRoot::new_local();
53 source_root.insert_file(rel_path.clone(), file_id); 53 source_root.insert_file(rel_path.clone(), file_id);
54 54
55 let mut crate_graph = CrateGraph::default(); 55 let fixture = parse_single_fixture(ra_fixture);
56 crate_graph.add_crate_root( 56
57 file_id, 57 let crate_graph = if let Some(entry) = fixture {
58 Edition::Edition2018, 58 let meta = match parse_meta(&entry.meta) {
59 None, 59 ParsedMeta::File(it) => it,
60 CfgOptions::default(), 60 _ => panic!("with_single_file only support file meta"),
61 Env::default(), 61 };
62 ); 62
63 63 let mut crate_graph = CrateGraph::default();
64 db.set_file_text(file_id, Arc::new(text.to_string())); 64 crate_graph.add_crate_root(file_id, meta.edition, meta.krate, meta.cfg, meta.env);
65 crate_graph
66 } else {
67 let mut crate_graph = CrateGraph::default();
68 crate_graph.add_crate_root(
69 file_id,
70 Edition::Edition2018,
71 None,
72 CfgOptions::default(),
73 Env::default(),
74 );
75 crate_graph
76 };
77
78 db.set_file_text(file_id, Arc::new(ra_fixture.to_string()));
65 db.set_file_relative_path(file_id, rel_path); 79 db.set_file_relative_path(file_id, rel_path);
66 db.set_file_source_root(file_id, WORKSPACE); 80 db.set_file_source_root(file_id, WORKSPACE);
67 db.set_source_root(WORKSPACE, Arc::new(source_root)); 81 db.set_source_root(WORKSPACE, Arc::new(source_root));
@@ -104,7 +118,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit
104 meta.edition, 118 meta.edition,
105 Some(krate.clone()), 119 Some(krate.clone()),
106 meta.cfg, 120 meta.cfg,
107 Env::default(), 121 meta.env,
108 ); 122 );
109 let prev = crates.insert(krate.clone(), crate_id); 123 let prev = crates.insert(krate.clone(), crate_id);
110 assert!(prev.is_none()); 124 assert!(prev.is_none());
@@ -167,9 +181,10 @@ struct FileMeta {
167 deps: Vec<String>, 181 deps: Vec<String>,
168 cfg: CfgOptions, 182 cfg: CfgOptions,
169 edition: Edition, 183 edition: Edition,
184 env: Env,
170} 185}
171 186
172//- /lib.rs crate:foo deps:bar,baz 187//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo)
173fn parse_meta(meta: &str) -> ParsedMeta { 188fn parse_meta(meta: &str) -> ParsedMeta {
174 let components = meta.split_ascii_whitespace().collect::<Vec<_>>(); 189 let components = meta.split_ascii_whitespace().collect::<Vec<_>>();
175 190
@@ -186,6 +201,7 @@ fn parse_meta(meta: &str) -> ParsedMeta {
186 let mut deps = Vec::new(); 201 let mut deps = Vec::new();
187 let mut edition = Edition::Edition2018; 202 let mut edition = Edition::Edition2018;
188 let mut cfg = CfgOptions::default(); 203 let mut cfg = CfgOptions::default();
204 let mut env = Env::default();
189 for component in components[1..].iter() { 205 for component in components[1..].iter() {
190 let (key, value) = split1(component, ':').unwrap(); 206 let (key, value) = split1(component, ':').unwrap();
191 match key { 207 match key {
@@ -200,11 +216,18 @@ fn parse_meta(meta: &str) -> ParsedMeta {
200 } 216 }
201 } 217 }
202 } 218 }
219 "env" => {
220 for key in value.split(',') {
221 if let Some((k, v)) = split1(key, '=') {
222 env.set(k.into(), v.into());
223 }
224 }
225 }
203 _ => panic!("bad component: {:?}", component), 226 _ => panic!("bad component: {:?}", component),
204 } 227 }
205 } 228 }
206 229
207 ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg }) 230 ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg, env })
208} 231}
209 232
210fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { 233fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> {
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index b77640b2b..1a1c64202 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -261,6 +261,16 @@ impl fmt::Display for Edition {
261 } 261 }
262} 262}
263 263
264impl Env {
265 pub fn set(&mut self, env: &str, value: String) {
266 self.entries.insert(env.to_owned(), value);
267 }
268
269 pub fn get(&self, env: &str) -> Option<String> {
270 self.entries.get(env).cloned()
271 }
272}
273
264#[derive(Debug)] 274#[derive(Debug)]
265pub struct ParseEditionError { 275pub struct ParseEditionError {
266 invalid_input: String, 276 invalid_input: String,
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index b7e8855fb..7f5e1469e 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -516,9 +516,31 @@ pub(crate) fn inherent_impl_substs(
516 let self_ty_with_vars = 516 let self_ty_with_vars =
517 Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars }; 517 Canonical { num_vars: vars.len() + self_ty.num_vars, value: self_ty_with_vars };
518 let substs = super::infer::unify(&self_ty_with_vars, self_ty); 518 let substs = super::infer::unify(&self_ty_with_vars, self_ty);
519 // we only want the substs for the vars we added, not the ones from self_ty 519 // We only want the substs for the vars we added, not the ones from self_ty.
520 let result = substs.map(|s| s.suffix(vars.len())); 520 // Also, if any of the vars we added are still in there, we replace them by
521 result 521 // Unknown. I think this can only really happen if self_ty contained
522 // Unknown, and in that case we want the result to contain Unknown in those
523 // places again.
524 substs.map(|s| fallback_bound_vars(s.suffix(vars.len()), self_ty.num_vars))
525}
526
527/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
528/// num_vars_to_keep) by `Ty::Unknown`.
529fn fallback_bound_vars(s: Substs, num_vars_to_keep: usize) -> Substs {
530 s.fold_binders(
531 &mut |ty, binders| {
532 if let Ty::Bound(idx) = &ty {
533 if *idx >= binders as u32 {
534 Ty::Unknown
535 } else {
536 ty
537 }
538 } else {
539 ty
540 }
541 },
542 num_vars_to_keep,
543 )
522} 544}
523 545
524fn transform_receiver_ty( 546fn transform_receiver_ty(
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index c378c2c62..a27e0fc15 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -33,6 +33,23 @@ pub use crate::completion::completion_item::{
33 CompletionItem, CompletionItemKind, InsertTextFormat, 33 CompletionItem, CompletionItemKind, InsertTextFormat,
34}; 34};
35 35
36#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct CompletionOptions {
38 pub enable_postfix_completions: bool,
39 pub add_call_parenthesis: bool,
40 pub add_call_argument_snippets: bool,
41}
42
43impl Default for CompletionOptions {
44 fn default() -> Self {
45 CompletionOptions {
46 enable_postfix_completions: true,
47 add_call_parenthesis: true,
48 add_call_argument_snippets: true,
49 }
50 }
51}
52
36/// Main entry point for completion. We run completion as a two-phase process. 53/// Main entry point for completion. We run completion as a two-phase process.
37/// 54///
38/// First, we look at the position and collect a so-called `CompletionContext. 55/// First, we look at the position and collect a so-called `CompletionContext.
@@ -55,8 +72,12 @@ pub use crate::completion::completion_item::{
55/// `foo` *should* be present among the completion variants. Filtering by 72/// `foo` *should* be present among the completion variants. Filtering by
56/// identifier prefix/fuzzy match should be done higher in the stack, together 73/// identifier prefix/fuzzy match should be done higher in the stack, together
57/// with ordering of completions (currently this is done by the client). 74/// with ordering of completions (currently this is done by the client).
58pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<Completions> { 75pub(crate) fn completions(
59 let ctx = CompletionContext::new(db, position)?; 76 db: &RootDatabase,
77 position: FilePosition,
78 opts: &CompletionOptions,
79) -> Option<Completions> {
80 let ctx = CompletionContext::new(db, position, opts)?;
60 81
61 let mut acc = Completions::default(); 82 let mut acc = Completions::default();
62 83
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index f275305e2..d8f6f0d9d 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -718,4 +718,35 @@ mod tests {
718 "### 718 "###
719 ); 719 );
720 } 720 }
721
722 #[test]
723 fn test_method_completion_3547() {
724 assert_debug_snapshot!(
725 do_ref_completion(
726 r"
727 struct HashSet<T> {}
728 impl<T> HashSet<T> {
729 pub fn the_method(&self) {}
730 }
731 fn foo() {
732 let s: HashSet<_>;
733 s.<|>
734 }
735 ",
736 ),
737 @r###"
738 [
739 CompletionItem {
740 label: "the_method()",
741 source_range: [201; 201),
742 delete: [201; 201),
743 insert: "the_method()$0",
744 kind: Method,
745 lookup: "the_method",
746 detail: "pub fn the_method(&self)",
747 },
748 ]
749 "###
750 );
751 }
721} 752}
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 65ecea125..6d000548d 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -12,7 +12,7 @@ use crate::{
12}; 12};
13 13
14pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 14pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
15 if !ctx.db.feature_flags.get("completion.enable-postfix") { 15 if !ctx.options.enable_postfix_completions {
16 return; 16 return;
17 } 17 }
18 18
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 18a1d2995..2bf654a57 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -34,7 +34,7 @@
34use hir::{self, Docs, HasSource}; 34use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_impl_items; 35use ra_assists::utils::get_missing_impl_items;
36use ra_syntax::{ 36use ra_syntax::{
37 ast::{self, edit}, 37 ast::{self, edit, ImplDef},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, 38 AstNode, SyntaxKind, SyntaxNode, TextRange,
39}; 39};
40use ra_text_edit::TextEdit; 40use ra_text_edit::TextEdit;
@@ -47,22 +47,22 @@ use crate::{
47}; 47};
48 48
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 let trigger = ctx.token.ancestors().find(|p| match p.kind() { 50 if let Some((trigger, impl_def)) = completion_match(ctx) {
51 SyntaxKind::FN_DEF
52 | SyntaxKind::TYPE_ALIAS_DEF
53 | SyntaxKind::CONST_DEF
54 | SyntaxKind::BLOCK_EXPR => true,
55 _ => false,
56 });
57
58 let impl_def = trigger
59 .as_ref()
60 .and_then(|node| node.parent())
61 .and_then(|node| node.parent())
62 .and_then(ast::ImplDef::cast);
63
64 if let (Some(trigger), Some(impl_def)) = (trigger, impl_def) {
65 match trigger.kind() { 51 match trigger.kind() {
52 SyntaxKind::NAME_REF => {
53 get_missing_impl_items(&ctx.sema, &impl_def).iter().for_each(|item| match item {
54 hir::AssocItem::Function(fn_item) => {
55 add_function_impl(&trigger, acc, ctx, &fn_item)
56 }
57 hir::AssocItem::TypeAlias(type_item) => {
58 add_type_alias_impl(&trigger, acc, ctx, &type_item)
59 }
60 hir::AssocItem::Const(const_item) => {
61 add_const_impl(&trigger, acc, ctx, &const_item)
62 }
63 })
64 }
65
66 SyntaxKind::FN_DEF => { 66 SyntaxKind::FN_DEF => {
67 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map( 67 for missing_fn in get_missing_impl_items(&ctx.sema, &impl_def).iter().filter_map(
68 |item| match item { 68 |item| match item {
@@ -101,6 +101,21 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
101 } 101 }
102} 102}
103 103
104fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, ImplDef)> {
105 let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() {
106 SyntaxKind::FN_DEF
107 | SyntaxKind::TYPE_ALIAS_DEF
108 | SyntaxKind::CONST_DEF
109 | SyntaxKind::BLOCK_EXPR => Some((p, 2)),
110 SyntaxKind::NAME_REF => Some((p, 5)),
111 _ => None,
112 })?;
113 let impl_def = (0..impl_def_offset - 1)
114 .try_fold(trigger.parent()?, |t, _| t.parent())
115 .and_then(ast::ImplDef::cast)?;
116 Some((trigger, impl_def))
117}
118
104fn add_function_impl( 119fn add_function_impl(
105 fn_def_node: &SyntaxNode, 120 fn_def_node: &SyntaxNode,
106 acc: &mut Completions, 121 acc: &mut Completions,
@@ -210,6 +225,103 @@ mod tests {
210 } 225 }
211 226
212 #[test] 227 #[test]
228 fn name_ref_function_type_const() {
229 let completions = complete(
230 r"
231 trait Test {
232 type TestType;
233 const TEST_CONST: u16;
234 fn test();
235 }
236
237 struct T1;
238
239 impl Test for T1 {
240 t<|>
241 }
242 ",
243 );
244 assert_debug_snapshot!(completions, @r###"
245 [
246 CompletionItem {
247 label: "const TEST_CONST: u16 = ",
248 source_range: [209; 210),
249 delete: [209; 210),
250 insert: "const TEST_CONST: u16 = ",
251 kind: Const,
252 lookup: "TEST_CONST",
253 },
254 CompletionItem {
255 label: "fn test()",
256 source_range: [209; 210),
257 delete: [209; 210),
258 insert: "fn test() {}",
259 kind: Function,
260 lookup: "test",
261 },
262 CompletionItem {
263 label: "type TestType = ",
264 source_range: [209; 210),
265 delete: [209; 210),
266 insert: "type TestType = ",
267 kind: TypeAlias,
268 lookup: "TestType",
269 },
270 ]
271 "###);
272 }
273
274 #[test]
275 fn no_nested_fn_completions() {
276 let completions = complete(
277 r"
278 trait Test {
279 fn test();
280 fn test2();
281 }
282
283 struct T1;
284
285 impl Test for T1 {
286 fn test() {
287 t<|>
288 }
289 }
290 ",
291 );
292 assert_debug_snapshot!(completions, @r###"[]"###);
293 }
294
295 #[test]
296 fn name_ref_single_function() {
297 let completions = complete(
298 r"
299 trait Test {
300 fn test();
301 }
302
303 struct T1;
304
305 impl Test for T1 {
306 t<|>
307 }
308 ",
309 );
310 assert_debug_snapshot!(completions, @r###"
311 [
312 CompletionItem {
313 label: "fn test()",
314 source_range: [139; 140),
315 delete: [139; 140),
316 insert: "fn test() {}",
317 kind: Function,
318 lookup: "test",
319 },
320 ]
321 "###);
322 }
323
324 #[test]
213 fn single_function() { 325 fn single_function() {
214 let completions = complete( 326 let completions = complete(
215 r" 327 r"
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 40535c09e..3646fb8dc 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::AtomTextEdit;
13 13
14use crate::FilePosition; 14use crate::{completion::CompletionOptions, FilePosition};
15 15
16/// `CompletionContext` is created early during completion to figure out, where 16/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 17/// exactly is the cursor, syntax-wise.
@@ -19,6 +19,7 @@ use crate::FilePosition;
19pub(crate) struct CompletionContext<'a> { 19pub(crate) struct CompletionContext<'a> {
20 pub(super) sema: Semantics<'a, RootDatabase>, 20 pub(super) sema: Semantics<'a, RootDatabase>,
21 pub(super) db: &'a RootDatabase, 21 pub(super) db: &'a RootDatabase,
22 pub(super) options: &'a CompletionOptions,
22 pub(super) offset: TextUnit, 23 pub(super) offset: TextUnit,
23 /// The token before the cursor, in the original file. 24 /// The token before the cursor, in the original file.
24 pub(super) original_token: SyntaxToken, 25 pub(super) original_token: SyntaxToken,
@@ -57,6 +58,7 @@ impl<'a> CompletionContext<'a> {
57 pub(super) fn new( 58 pub(super) fn new(
58 db: &'a RootDatabase, 59 db: &'a RootDatabase,
59 position: FilePosition, 60 position: FilePosition,
61 options: &'a CompletionOptions,
60 ) -> Option<CompletionContext<'a>> { 62 ) -> Option<CompletionContext<'a>> {
61 let sema = Semantics::new(db); 63 let sema = Semantics::new(db);
62 64
@@ -80,6 +82,7 @@ impl<'a> CompletionContext<'a> {
80 let mut ctx = CompletionContext { 82 let mut ctx = CompletionContext {
81 sema, 83 sema,
82 db, 84 db,
85 options,
83 original_token, 86 original_token,
84 token, 87 token,
85 offset: position.offset, 88 offset: position.offset,
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index 19bbb2517..1d14e9636 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -321,14 +321,18 @@ impl Into<Vec<CompletionItem>> for Completions {
321 321
322#[cfg(test)] 322#[cfg(test)]
323pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 323pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
324 use crate::completion::completions; 324 use crate::{
325 use crate::mock_analysis::{analysis_and_position, single_file_with_position}; 325 completion::{completions, CompletionOptions},
326 mock_analysis::{analysis_and_position, single_file_with_position},
327 };
328
326 let (analysis, position) = if code.contains("//-") { 329 let (analysis, position) = if code.contains("//-") {
327 analysis_and_position(code) 330 analysis_and_position(code)
328 } else { 331 } else {
329 single_file_with_position(code) 332 single_file_with_position(code)
330 }; 333 };
331 let completions = completions(&analysis.db, position).unwrap(); 334 let options = CompletionOptions::default();
335 let completions = completions(&analysis.db, position, &options).unwrap();
332 let completion_items: Vec<CompletionItem> = completions.into(); 336 let completion_items: Vec<CompletionItem> = completions.into();
333 let mut kind_completions: Vec<CompletionItem> = 337 let mut kind_completions: Vec<CompletionItem> =
334 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 338 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect();
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index aada4d025..3dc56e4a3 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -104,10 +104,7 @@ impl Completions {
104 }; 104 };
105 105
106 // Add `<>` for generic types 106 // Add `<>` for generic types
107 if ctx.is_path_type 107 if ctx.is_path_type && !ctx.has_type_args && ctx.options.add_call_parenthesis {
108 && !ctx.has_type_args
109 && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
110 {
111 let has_non_default_type_params = match resolution { 108 let has_non_default_type_params = match resolution {
112 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), 109 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
113 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), 110 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
@@ -212,21 +209,14 @@ impl Completions {
212 .detail(function_signature.to_string()); 209 .detail(function_signature.to_string());
213 210
214 // If not an import, add parenthesis automatically. 211 // If not an import, add parenthesis automatically.
215 if ctx.use_item_syntax.is_none() 212 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.options.add_call_parenthesis {
216 && !ctx.is_call
217 && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
218 {
219 tested_by!(inserts_parens_for_function_calls); 213 tested_by!(inserts_parens_for_function_calls);
220 214
221 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 { 215 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 {
222 (format!("{}()$0", name), format!("{}()", name)) 216 (format!("{}()$0", name), format!("{}()", name))
223 } else { 217 } else {
224 builder = builder.trigger_call_info(); 218 builder = builder.trigger_call_info();
225 let snippet = if ctx 219 let snippet = if ctx.options.add_call_argument_snippets {
226 .db
227 .feature_flags
228 .get("completion.insertion.add-argument-snippets")
229 {
230 let to_skip = if has_self_param { 1 } else { 0 }; 220 let to_skip = if has_self_param { 1 } else { 0 };
231 let function_params_snippet = join( 221 let function_params_snippet = join(
232 function_signature.parameter_names.iter().skip(to_skip).enumerate().map( 222 function_signature.parameter_names.iter().skip(to_skip).enumerate().map(
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index c60e86aea..9f45003d3 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -62,7 +62,7 @@ use crate::display::ToNav;
62pub use crate::{ 62pub use crate::{
63 assists::{Assist, AssistId}, 63 assists::{Assist, AssistId},
64 call_hierarchy::CallItem, 64 call_hierarchy::CallItem,
65 completion::{CompletionItem, CompletionItemKind, InsertTextFormat}, 65 completion::{CompletionItem, CompletionItemKind, CompletionOptions, InsertTextFormat},
66 diagnostics::Severity, 66 diagnostics::Severity,
67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
68 expand_macro::ExpandedMacro, 68 expand_macro::ExpandedMacro,
@@ -84,7 +84,6 @@ pub use ra_db::{
84}; 84};
85pub use ra_ide_db::{ 85pub use ra_ide_db::{
86 change::{AnalysisChange, LibraryData}, 86 change::{AnalysisChange, LibraryData},
87 feature_flags::FeatureFlags,
88 line_index::{LineCol, LineIndex}, 87 line_index::{LineCol, LineIndex},
89 line_index_utils::translate_offset_with_edit, 88 line_index_utils::translate_offset_with_edit,
90 search::SearchScope, 89 search::SearchScope,
@@ -131,13 +130,13 @@ pub struct AnalysisHost {
131 130
132impl Default for AnalysisHost { 131impl Default for AnalysisHost {
133 fn default() -> AnalysisHost { 132 fn default() -> AnalysisHost {
134 AnalysisHost::new(None, FeatureFlags::default()) 133 AnalysisHost::new(None)
135 } 134 }
136} 135}
137 136
138impl AnalysisHost { 137impl AnalysisHost {
139 pub fn new(lru_capcity: Option<usize>, feature_flags: FeatureFlags) -> AnalysisHost { 138 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
140 AnalysisHost { db: RootDatabase::new(lru_capcity, feature_flags) } 139 AnalysisHost { db: RootDatabase::new(lru_capacity) }
141 } 140 }
142 /// Returns a snapshot of the current state, which you can query for 141 /// Returns a snapshot of the current state, which you can query for
143 /// semantic information. 142 /// semantic information.
@@ -145,10 +144,6 @@ impl AnalysisHost {
145 Analysis { db: self.db.snapshot() } 144 Analysis { db: self.db.snapshot() }
146 } 145 }
147 146
148 pub fn feature_flags(&self) -> &FeatureFlags {
149 &self.db.feature_flags
150 }
151
152 /// Applies changes to the current state of the world. If there are 147 /// Applies changes to the current state of the world. If there are
153 /// outstanding snapshots, they will be canceled. 148 /// outstanding snapshots, they will be canceled.
154 pub fn apply_change(&mut self, change: AnalysisChange) { 149 pub fn apply_change(&mut self, change: AnalysisChange) {
@@ -224,11 +219,6 @@ impl Analysis {
224 (host.analysis(), file_id) 219 (host.analysis(), file_id)
225 } 220 }
226 221
227 /// Features for Analysis.
228 pub fn feature_flags(&self) -> &FeatureFlags {
229 &self.db.feature_flags
230 }
231
232 /// Debug info about the current state of the analysis. 222 /// Debug info about the current state of the analysis.
233 pub fn status(&self) -> Cancelable<String> { 223 pub fn status(&self) -> Cancelable<String> {
234 self.with_db(|db| status::status(&*db)) 224 self.with_db(|db| status::status(&*db))
@@ -450,8 +440,12 @@ impl Analysis {
450 } 440 }
451 441
452 /// Computes completions at the given position. 442 /// Computes completions at the given position.
453 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 443 pub fn completions(
454 self.with_db(|db| completion::completions(db, position).map(Into::into)) 444 &self,
445 position: FilePosition,
446 options: &CompletionOptions,
447 ) -> Cancelable<Option<Vec<CompletionItem>>> {
448 self.with_db(|db| completion::completions(db, position, options).map(Into::into))
455 } 449 }
456 450
457 /// Computes assists (aka code actions aka intentions) for the given 451 /// Computes assists (aka code actions aka intentions) for the given
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml
index 52f0f23df..de4f5bce0 100644
--- a/crates/ra_ide_db/Cargo.toml
+++ b/crates/ra_ide_db/Cargo.toml
@@ -13,7 +13,7 @@ wasm = []
13[dependencies] 13[dependencies]
14log = "0.4.8" 14log = "0.4.8"
15rayon = "1.3.0" 15rayon = "1.3.0"
16fst = { version = "0.3.5", default-features = false } 16fst = { version = "0.4", default-features = false }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18superslice = "1.0.0" 18superslice = "1.0.0"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index a105c7556..6bcccc848 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -5,7 +5,6 @@
5pub mod marks; 5pub mod marks;
6pub mod line_index; 6pub mod line_index;
7pub mod line_index_utils; 7pub mod line_index_utils;
8pub mod feature_flags;
9pub mod symbol_index; 8pub mod symbol_index;
10pub mod change; 9pub mod change;
11pub mod defs; 10pub mod defs;
@@ -22,7 +21,7 @@ use ra_db::{
22}; 21};
23use rustc_hash::FxHashMap; 22use rustc_hash::FxHashMap;
24 23
25use crate::{feature_flags::FeatureFlags, line_index::LineIndex, symbol_index::SymbolsDatabase}; 24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
26 25
27#[salsa::database( 26#[salsa::database(
28 ra_db::SourceDatabaseStorage, 27 ra_db::SourceDatabaseStorage,
@@ -37,7 +36,6 @@ use crate::{feature_flags::FeatureFlags, line_index::LineIndex, symbol_index::Sy
37#[derive(Debug)] 36#[derive(Debug)]
38pub struct RootDatabase { 37pub struct RootDatabase {
39 runtime: salsa::Runtime<RootDatabase>, 38 runtime: salsa::Runtime<RootDatabase>,
40 pub feature_flags: Arc<FeatureFlags>,
41 pub(crate) debug_data: Arc<DebugData>, 39 pub(crate) debug_data: Arc<DebugData>,
42 pub last_gc: crate::wasm_shims::Instant, 40 pub last_gc: crate::wasm_shims::Instant,
43 pub last_gc_check: crate::wasm_shims::Instant, 41 pub last_gc_check: crate::wasm_shims::Instant,
@@ -82,17 +80,16 @@ impl salsa::Database for RootDatabase {
82 80
83impl Default for RootDatabase { 81impl Default for RootDatabase {
84 fn default() -> RootDatabase { 82 fn default() -> RootDatabase {
85 RootDatabase::new(None, FeatureFlags::default()) 83 RootDatabase::new(None)
86 } 84 }
87} 85}
88 86
89impl RootDatabase { 87impl RootDatabase {
90 pub fn new(lru_capacity: Option<usize>, feature_flags: FeatureFlags) -> RootDatabase { 88 pub fn new(lru_capacity: Option<usize>) -> RootDatabase {
91 let mut db = RootDatabase { 89 let mut db = RootDatabase {
92 runtime: salsa::Runtime::default(), 90 runtime: salsa::Runtime::default(),
93 last_gc: crate::wasm_shims::Instant::now(), 91 last_gc: crate::wasm_shims::Instant::now(),
94 last_gc_check: crate::wasm_shims::Instant::now(), 92 last_gc_check: crate::wasm_shims::Instant::now(),
95 feature_flags: Arc::new(feature_flags),
96 debug_data: Default::default(), 93 debug_data: Default::default(),
97 }; 94 };
98 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 95 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
@@ -112,7 +109,6 @@ impl salsa::ParallelDatabase for RootDatabase {
112 runtime: self.runtime.snapshot(self), 109 runtime: self.runtime.snapshot(self),
113 last_gc: self.last_gc, 110 last_gc: self.last_gc,
114 last_gc_check: self.last_gc_check, 111 last_gc_check: self.last_gc_check,
115 feature_flags: Arc::clone(&self.feature_flags),
116 debug_data: Arc::clone(&self.debug_data), 112 debug_data: Arc::clone(&self.debug_data),
117 }) 113 })
118 } 114 }
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index e6b3126b6..884359ee3 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -163,7 +163,7 @@ pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymb
163#[derive(Default)] 163#[derive(Default)]
164pub struct SymbolIndex { 164pub struct SymbolIndex {
165 symbols: Vec<FileSymbol>, 165 symbols: Vec<FileSymbol>,
166 map: fst::Map, 166 map: fst::Map<Vec<u8>>,
167} 167}
168 168
169impl fmt::Debug for SymbolIndex { 169impl fmt::Debug for SymbolIndex {
@@ -221,7 +221,7 @@ impl SymbolIndex {
221 builder.insert(key, value).unwrap(); 221 builder.insert(key, value).unwrap();
222 } 222 }
223 223
224 let map = fst::Map::from_bytes(builder.into_inner().unwrap()).unwrap(); 224 let map = fst::Map::new(builder.into_inner().unwrap()).unwrap();
225 SymbolIndex { symbols, map } 225 SymbolIndex { symbols, map }
226 } 226 }
227 227
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 91855e592..28a23934f 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -12,7 +12,7 @@ use ra_db::{
12 salsa::{Database, Durability}, 12 salsa::{Database, Durability},
13 FileId, SourceDatabaseExt, 13 FileId, SourceDatabaseExt,
14}; 14};
15use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FilePosition, LineCol}; 15use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionOptions, FilePosition, LineCol};
16 16
17use crate::cli::{load_cargo::load_cargo, Verbosity}; 17use crate::cli::{load_cargo::load_cargo, Verbosity};
18 18
@@ -94,17 +94,19 @@ pub fn analysis_bench(verbosity: Verbosity, path: &Path, what: BenchWhat) -> Res
94 .analysis() 94 .analysis()
95 .file_line_index(file_id)? 95 .file_line_index(file_id)?
96 .offset(LineCol { line: pos.line - 1, col_utf16: pos.column }); 96 .offset(LineCol { line: pos.line - 1, col_utf16: pos.column });
97 let file_postion = FilePosition { file_id, offset }; 97 let file_position = FilePosition { file_id, offset };
98 98
99 if is_completion { 99 if is_completion {
100 let res = 100 let options = CompletionOptions::default();
101 do_work(&mut host, file_id, |analysis| analysis.completions(file_postion)); 101 let res = do_work(&mut host, file_id, |analysis| {
102 analysis.completions(file_position, &options)
103 });
102 if verbosity.is_verbose() { 104 if verbosity.is_verbose() {
103 println!("\n{:#?}", res); 105 println!("\n{:#?}", res);
104 } 106 }
105 } else { 107 } else {
106 let res = 108 let res =
107 do_work(&mut host, file_id, |analysis| analysis.goto_definition(file_postion)); 109 do_work(&mut host, file_id, |analysis| analysis.goto_definition(file_position));
108 if verbosity.is_verbose() { 110 if verbosity.is_verbose() {
109 println!("\n{:#?}", res); 111 println!("\n{:#?}", res);
110 } 112 }
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 4be987860..5df29a383 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -6,7 +6,7 @@ use std::path::Path;
6use anyhow::Result; 6use anyhow::Result;
7use crossbeam_channel::{unbounded, Receiver}; 7use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{CrateGraph, FileId, SourceRootId}; 8use ra_db::{CrateGraph, FileId, SourceRootId};
9use ra_ide::{AnalysisChange, AnalysisHost, FeatureFlags}; 9use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace}; 10use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace};
11use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 11use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
12use rustc_hash::{FxHashMap, FxHashSet}; 12use rustc_hash::{FxHashMap, FxHashSet};
@@ -82,7 +82,7 @@ pub(crate) fn load(
82 receiver: Receiver<VfsTask>, 82 receiver: Receiver<VfsTask>,
83) -> AnalysisHost { 83) -> AnalysisHost {
84 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); 84 let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
85 let mut host = AnalysisHost::new(lru_cap, FeatureFlags::default()); 85 let mut host = AnalysisHost::new(lru_cap);
86 let mut analysis_change = AnalysisChange::new(); 86 let mut analysis_change = AnalysisChange::new();
87 analysis_change.set_crate_graph(crate_graph); 87 analysis_change.set_crate_graph(crate_graph);
88 88
diff --git a/crates/ra_ide_db/src/feature_flags.rs b/crates/rust-analyzer/src/feature_flags.rs
index 968415072..dbb3f50a0 100644
--- a/crates/ra_ide_db/src/feature_flags.rs
+++ b/crates/rust-analyzer/src/feature_flags.rs
@@ -2,6 +2,10 @@
2 2
3use rustc_hash::FxHashMap; 3use rustc_hash::FxHashMap;
4 4
5// FIXME: looks like a much better design is to pass options to each call,
6// rather than to have a global ambient feature flags -- that way, the clients
7// can issue two successive calls with different options.
8
5/// Feature flags hold fine-grained toggles for all *user-visible* features of 9/// Feature flags hold fine-grained toggles for all *user-visible* features of
6/// rust-analyzer. 10/// rust-analyzer.
7/// 11///
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index a0f968823..e50e47b19 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -37,6 +37,7 @@ mod config;
37mod world; 37mod world;
38mod diagnostics; 38mod diagnostics;
39mod semantic_tokens; 39mod semantic_tokens;
40mod feature_flags;
40 41
41use serde::de::DeserializeOwned; 42use serde::de::DeserializeOwned;
42 43
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 221f464b6..f9de712a0 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -18,7 +18,7 @@ use crossbeam_channel::{select, unbounded, RecvError, Sender};
18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
19use lsp_types::{ClientCapabilities, NumberOrString}; 19use lsp_types::{ClientCapabilities, NumberOrString};
20use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask}; 20use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask};
21use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; 21use ra_ide::{Canceled, FileId, LibraryData, SourceRootId};
22use ra_prof::profile; 22use ra_prof::profile;
23use ra_vfs::{VfsFile, VfsTask, Watch}; 23use ra_vfs::{VfsFile, VfsTask, Watch};
24use relative_path::RelativePathBuf; 24use relative_path::RelativePathBuf;
@@ -28,6 +28,7 @@ use threadpool::ThreadPool;
28 28
29use crate::{ 29use crate::{
30 diagnostics::DiagnosticTask, 30 diagnostics::DiagnosticTask,
31 feature_flags::FeatureFlags,
31 main_loop::{ 32 main_loop::{
32 pending_requests::{PendingRequest, PendingRequests}, 33 pending_requests::{PendingRequest, PendingRequests},
33 subscriptions::Subscriptions, 34 subscriptions::Subscriptions,
@@ -423,7 +424,7 @@ fn loop_turn(
423 { 424 {
424 loop_state.workspace_loaded = true; 425 loop_state.workspace_loaded = true;
425 let n_packages: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum(); 426 let n_packages: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum();
426 if world_state.feature_flags().get("notifications.workspace-loaded") { 427 if world_state.feature_flags.get("notifications.workspace-loaded") {
427 let msg = format!("workspace loaded, {} rust packages", n_packages); 428 let msg = format!("workspace loaded, {} rust packages", n_packages);
428 show_message(req::MessageType::Info, msg, &connection.sender); 429 show_message(req::MessageType::Info, msg, &connection.sender);
429 } 430 }
@@ -839,7 +840,7 @@ fn update_file_notifications_on_threadpool(
839 subscriptions: Vec<FileId>, 840 subscriptions: Vec<FileId>,
840) { 841) {
841 log::trace!("updating notifications for {:?}", subscriptions); 842 log::trace!("updating notifications for {:?}", subscriptions);
842 let publish_diagnostics = world.feature_flags().get("lsp.diagnostics"); 843 let publish_diagnostics = world.feature_flags.get("lsp.diagnostics");
843 pool.execute(move || { 844 pool.execute(move || {
844 for file_id in subscriptions { 845 for file_id in subscriptions {
845 if publish_diagnostics { 846 if publish_diagnostics {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 8dc6e8dc0..fcb40432d 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -20,8 +20,8 @@ use lsp_types::{
20 TextEdit, WorkspaceEdit, 20 TextEdit, WorkspaceEdit,
21}; 21};
22use ra_ide::{ 22use ra_ide::{
23 Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, 23 Assist, AssistId, CompletionOptions, FileId, FilePosition, FileRange, Query, RangeInfo,
24 SearchScope, 24 Runnable, RunnableKind, SearchScope,
25}; 25};
26use ra_prof::profile; 26use ra_prof::profile;
27use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; 27use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit};
@@ -424,7 +424,15 @@ pub fn handle_completion(
424 return Ok(None); 424 return Ok(None);
425 } 425 }
426 426
427 let items = match world.analysis().completions(position)? { 427 let options = CompletionOptions {
428 enable_postfix_completions: world.feature_flags.get("completion.enable-postfix"),
429 add_call_parenthesis: world.feature_flags.get("completion.insertion.add-call-parenthesis"),
430 add_call_argument_snippets: world
431 .feature_flags
432 .get("completion.insertion.add-argument-snippets"),
433 };
434
435 let items = match world.analysis().completions(position, &options)? {
428 None => return Ok(None), 436 None => return Ok(None),
429 Some(items) => items, 437 Some(items) => items,
430 }; 438 };
@@ -461,7 +469,7 @@ pub fn handle_signature_help(
461 let _p = profile("handle_signature_help"); 469 let _p = profile("handle_signature_help");
462 let position = params.try_conv_with(&world)?; 470 let position = params.try_conv_with(&world)?;
463 if let Some(call_info) = world.analysis().call_info(position)? { 471 if let Some(call_info) = world.analysis().call_info(position)? {
464 let concise = !world.analysis().feature_flags().get("call-info.full"); 472 let concise = !world.feature_flags.get("call-info.full");
465 let mut active_parameter = call_info.active_parameter.map(|it| it as i64); 473 let mut active_parameter = call_info.active_parameter.map(|it| it as i64);
466 if concise && call_info.signature.has_self_param { 474 if concise && call_info.signature.has_self_param {
467 active_parameter = active_parameter.map(|it| it.saturating_sub(1)); 475 active_parameter = active_parameter.map(|it| it.saturating_sub(1));
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 6f394055a..1ddc3c1a5 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -13,8 +13,7 @@ use lsp_types::Url;
13use parking_lot::RwLock; 13use parking_lot::RwLock;
14use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; 14use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher};
15use ra_ide::{ 15use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, 16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
17 SourceRootId,
18}; 17};
19use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; 18use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace};
20use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; 19use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
@@ -22,6 +21,7 @@ use relative_path::RelativePathBuf;
22 21
23use crate::{ 22use crate::{
24 diagnostics::{CheckFixes, DiagnosticCollection}, 23 diagnostics::{CheckFixes, DiagnosticCollection},
24 feature_flags::FeatureFlags,
25 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 25 main_loop::pending_requests::{CompletedRequest, LatestRequests},
26 vfs_glob::{Glob, RustPackageFilterBuilder}, 26 vfs_glob::{Glob, RustPackageFilterBuilder},
27 LspError, Result, 27 LspError, Result,
@@ -45,6 +45,7 @@ pub struct Options {
45#[derive(Debug)] 45#[derive(Debug)]
46pub struct WorldState { 46pub struct WorldState {
47 pub options: Options, 47 pub options: Options,
48 pub feature_flags: Arc<FeatureFlags>,
48 //FIXME: this belongs to `LoopState` rather than to `WorldState` 49 //FIXME: this belongs to `LoopState` rather than to `WorldState`
49 pub roots_to_scan: usize, 50 pub roots_to_scan: usize,
50 pub roots: Vec<PathBuf>, 51 pub roots: Vec<PathBuf>,
@@ -60,6 +61,7 @@ pub struct WorldState {
60/// An immutable snapshot of the world's state at a point in time. 61/// An immutable snapshot of the world's state at a point in time.
61pub struct WorldSnapshot { 62pub struct WorldSnapshot {
62 pub options: Options, 63 pub options: Options,
64 pub feature_flags: Arc<FeatureFlags>,
63 pub workspaces: Arc<Vec<ProjectWorkspace>>, 65 pub workspaces: Arc<Vec<ProjectWorkspace>>,
64 pub analysis: Analysis, 66 pub analysis: Analysis,
65 pub latest_requests: Arc<RwLock<LatestRequests>>, 67 pub latest_requests: Arc<RwLock<LatestRequests>>,
@@ -146,10 +148,11 @@ impl WorldState {
146 CheckWatcher::dummy() 148 CheckWatcher::dummy()
147 }); 149 });
148 150
149 let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags); 151 let mut analysis_host = AnalysisHost::new(lru_capacity);
150 analysis_host.apply_change(change); 152 analysis_host.apply_change(change);
151 WorldState { 153 WorldState {
152 options, 154 options,
155 feature_flags: Arc::new(feature_flags),
153 roots_to_scan, 156 roots_to_scan,
154 roots: folder_roots, 157 roots: folder_roots,
155 workspaces: Arc::new(workspaces), 158 workspaces: Arc::new(workspaces),
@@ -216,6 +219,7 @@ impl WorldState {
216 pub fn snapshot(&self) -> WorldSnapshot { 219 pub fn snapshot(&self) -> WorldSnapshot {
217 WorldSnapshot { 220 WorldSnapshot {
218 options: self.options.clone(), 221 options: self.options.clone(),
222 feature_flags: Arc::clone(&self.feature_flags),
219 workspaces: Arc::clone(&self.workspaces), 223 workspaces: Arc::clone(&self.workspaces),
220 analysis: self.analysis_host.analysis(), 224 analysis: self.analysis_host.analysis(),
221 vfs: Arc::clone(&self.vfs), 225 vfs: Arc::clone(&self.vfs),
@@ -235,10 +239,6 @@ impl WorldState {
235 pub fn complete_request(&mut self, request: CompletedRequest) { 239 pub fn complete_request(&mut self, request: CompletedRequest) {
236 self.latest_requests.write().record(request) 240 self.latest_requests.write().record(request)
237 } 241 }
238
239 pub fn feature_flags(&self) -> &FeatureFlags {
240 self.analysis_host.feature_flags()
241 }
242} 242}
243 243
244impl WorldSnapshot { 244impl WorldSnapshot {
@@ -306,8 +306,4 @@ impl WorldSnapshot {
306 let path = self.vfs.read().file2path(VfsFile(file_id.0)); 306 let path = self.vfs.read().file2path(VfsFile(file_id.0));
307 self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path)) 307 self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path))
308 } 308 }
309
310 pub fn feature_flags(&self) -> &FeatureFlags {
311 self.analysis.feature_flags()
312 }
313} 309}
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 69deddcb5..a0d8f4d37 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -202,6 +202,19 @@ pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
202 res 202 res
203} 203}
204 204
205/// Same as `parse_fixture`, except it allow empty fixture
206pub fn parse_single_fixture(fixture: &str) -> Option<FixtureEntry> {
207 if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) {
208 return None;
209 }
210
211 let fixtures = parse_fixture(fixture);
212 if fixtures.len() > 1 {
213 panic!("too many fixtures");
214 }
215 fixtures.into_iter().nth(0)
216}
217
205// Comparison functionality borrowed from cargo: 218// Comparison functionality borrowed from cargo:
206 219
207/// Compare a line with an expected pattern. 220/// Compare a line with an expected pattern.
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 77ee5a0cf..b07964546 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -114,9 +114,9 @@
114 } 114 }
115 }, 115 },
116 "@types/vscode": { 116 "@types/vscode": {
117 "version": "1.42.0", 117 "version": "1.43.0",
118 "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.42.0.tgz", 118 "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.43.0.tgz",
119 "integrity": "sha512-ds6TceMsh77Fs0Mq0Vap6Y72JbGWB8Bay4DrnJlf5d9ui2RSe1wis13oQm+XhguOeH1HUfLGzaDAoupTUtgabw==", 119 "integrity": "sha512-kIaR9qzd80rJOxePKpCB/mdy00mz8Apt2QA5Y6rdrKFn13QNFNeP3Hzmsf37Bwh/3cS7QjtAeGSK7wSqAU0sYQ==",
120 "dev": true 120 "dev": true
121 }, 121 },
122 "@typescript-eslint/eslint-plugin": { 122 "@typescript-eslint/eslint-plugin": {
diff --git a/editors/code/package.json b/editors/code/package.json
index 7a4a93e30..512885454 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -21,7 +21,7 @@
21 "Programming Languages" 21 "Programming Languages"
22 ], 22 ],
23 "engines": { 23 "engines": {
24 "vscode": "^1.42.0" 24 "vscode": "^1.43.0"
25 }, 25 },
26 "enableProposedApi": true, 26 "enableProposedApi": true,
27 "scripts": { 27 "scripts": {
@@ -41,7 +41,7 @@
41 "@rollup/plugin-node-resolve": "^7.1.1", 41 "@rollup/plugin-node-resolve": "^7.1.1",
42 "@types/node": "^12.12.29", 42 "@types/node": "^12.12.29",
43 "@types/node-fetch": "^2.5.5", 43 "@types/node-fetch": "^2.5.5",
44 "@types/vscode": "^1.42.0", 44 "@types/vscode": "^1.43.0",
45 "@typescript-eslint/eslint-plugin": "^2.22.0", 45 "@typescript-eslint/eslint-plugin": "^2.22.0",
46 "@typescript-eslint/parser": "^2.22.0", 46 "@typescript-eslint/parser": "^2.22.0",
47 "eslint": "^6.8.0", 47 "eslint": "^6.8.0",