aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-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
8 files changed, 207 insertions, 52 deletions
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