diff options
Diffstat (limited to 'crates/ra_ide')
-rw-r--r-- | crates/ra_ide/src/completion/complete_trait_impl.rs | 19 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_unqualified_path.rs | 40 | ||||
-rw-r--r-- | crates/ra_ide/src/diagnostics.rs | 72 | ||||
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 11 | ||||
-rw-r--r-- | crates/ra_ide/src/marks.rs | 1 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting/tests.rs | 12 |
7 files changed, 140 insertions, 18 deletions
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index fab02945c..2ec0e7ce9 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs | |||
@@ -142,11 +142,11 @@ fn add_function_impl( | |||
142 | CompletionItemKind::Function | 142 | CompletionItemKind::Function |
143 | }; | 143 | }; |
144 | 144 | ||
145 | let snippet = format!("{} {{}}", display); | 145 | let snippet = format!("{} {{\n $0\n}}", display); |
146 | 146 | ||
147 | let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end()); | 147 | let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end()); |
148 | 148 | ||
149 | builder.text_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); | 149 | builder.snippet_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc); |
150 | } | 150 | } |
151 | 151 | ||
152 | fn add_type_alias_impl( | 152 | fn add_type_alias_impl( |
@@ -217,9 +217,10 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String { | |||
217 | 217 | ||
218 | #[cfg(test)] | 218 | #[cfg(test)] |
219 | mod tests { | 219 | mod tests { |
220 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; | ||
221 | use insta::assert_debug_snapshot; | 220 | use insta::assert_debug_snapshot; |
222 | 221 | ||
222 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; | ||
223 | |||
223 | fn complete(code: &str) -> Vec<CompletionItem> { | 224 | fn complete(code: &str) -> Vec<CompletionItem> { |
224 | do_completion(code, CompletionKind::Magic) | 225 | do_completion(code, CompletionKind::Magic) |
225 | } | 226 | } |
@@ -255,7 +256,7 @@ mod tests { | |||
255 | label: "fn test()", | 256 | label: "fn test()", |
256 | source_range: [209; 210), | 257 | source_range: [209; 210), |
257 | delete: [209; 210), | 258 | delete: [209; 210), |
258 | insert: "fn test() {}", | 259 | insert: "fn test() {\n $0\n}", |
259 | kind: Function, | 260 | kind: Function, |
260 | lookup: "test", | 261 | lookup: "test", |
261 | }, | 262 | }, |
@@ -313,7 +314,7 @@ mod tests { | |||
313 | label: "fn test()", | 314 | label: "fn test()", |
314 | source_range: [139; 140), | 315 | source_range: [139; 140), |
315 | delete: [139; 140), | 316 | delete: [139; 140), |
316 | insert: "fn test() {}", | 317 | insert: "fn test() {\n $0\n}", |
317 | kind: Function, | 318 | kind: Function, |
318 | lookup: "test", | 319 | lookup: "test", |
319 | }, | 320 | }, |
@@ -342,7 +343,7 @@ mod tests { | |||
342 | label: "fn foo()", | 343 | label: "fn foo()", |
343 | source_range: [141; 142), | 344 | source_range: [141; 142), |
344 | delete: [138; 142), | 345 | delete: [138; 142), |
345 | insert: "fn foo() {}", | 346 | insert: "fn foo() {\n $0\n}", |
346 | kind: Function, | 347 | kind: Function, |
347 | lookup: "foo", | 348 | lookup: "foo", |
348 | }, | 349 | }, |
@@ -374,7 +375,7 @@ mod tests { | |||
374 | label: "fn foo_bar()", | 375 | label: "fn foo_bar()", |
375 | source_range: [200; 201), | 376 | source_range: [200; 201), |
376 | delete: [197; 201), | 377 | delete: [197; 201), |
377 | insert: "fn foo_bar() {}", | 378 | insert: "fn foo_bar() {\n $0\n}", |
378 | kind: Function, | 379 | kind: Function, |
379 | lookup: "foo_bar", | 380 | lookup: "foo_bar", |
380 | }, | 381 | }, |
@@ -425,7 +426,7 @@ mod tests { | |||
425 | label: "fn foo()", | 426 | label: "fn foo()", |
426 | source_range: [144; 145), | 427 | source_range: [144; 145), |
427 | delete: [141; 145), | 428 | delete: [141; 145), |
428 | insert: "fn foo<T>() {}", | 429 | insert: "fn foo<T>() {\n $0\n}", |
429 | kind: Function, | 430 | kind: Function, |
430 | lookup: "foo", | 431 | lookup: "foo", |
431 | }, | 432 | }, |
@@ -454,7 +455,7 @@ mod tests { | |||
454 | label: "fn foo()", | 455 | label: "fn foo()", |
455 | source_range: [166; 167), | 456 | source_range: [166; 167), |
456 | delete: [163; 167), | 457 | delete: [163; 167), |
457 | insert: "fn foo<T>()\nwhere T: Into<String> {}", | 458 | insert: "fn foo<T>()\nwhere T: Into<String> {\n $0\n}", |
458 | kind: Function, | 459 | kind: Function, |
459 | lookup: "foo", | 460 | lookup: "foo", |
460 | }, | 461 | }, |
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs index 2d8e0776c..ad5fdcc4e 100644 --- a/crates/ra_ide/src/completion/complete_unqualified_path.rs +++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs | |||
@@ -1,6 +1,10 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use hir::ScopeDef; | ||
4 | use test_utils::tested_by; | ||
5 | |||
3 | use crate::completion::{CompletionContext, Completions}; | 6 | use crate::completion::{CompletionContext, Completions}; |
7 | use ra_syntax::AstNode; | ||
4 | 8 | ||
5 | pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 9 | pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { |
6 | if !ctx.is_trivial_path { | 10 | if !ctx.is_trivial_path { |
@@ -14,12 +18,23 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
14 | return; | 18 | return; |
15 | } | 19 | } |
16 | 20 | ||
17 | ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res)); | 21 | ctx.scope().process_all_names(&mut |name, res| { |
22 | if ctx.use_item_syntax.is_some() { | ||
23 | if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { | ||
24 | if name_ref.syntax().text() == name.to_string().as_str() { | ||
25 | tested_by!(self_fulfilling_completion); | ||
26 | return; | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | acc.add_resolution(ctx, name.to_string(), &res) | ||
31 | }); | ||
18 | } | 32 | } |
19 | 33 | ||
20 | #[cfg(test)] | 34 | #[cfg(test)] |
21 | mod tests { | 35 | mod tests { |
22 | use insta::assert_debug_snapshot; | 36 | use insta::assert_debug_snapshot; |
37 | use test_utils::covers; | ||
23 | 38 | ||
24 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; | 39 | use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; |
25 | 40 | ||
@@ -28,6 +43,29 @@ mod tests { | |||
28 | } | 43 | } |
29 | 44 | ||
30 | #[test] | 45 | #[test] |
46 | fn self_fulfilling_completion() { | ||
47 | covers!(self_fulfilling_completion); | ||
48 | assert_debug_snapshot!( | ||
49 | do_reference_completion( | ||
50 | r#" | ||
51 | use foo<|> | ||
52 | use std::collections; | ||
53 | "#, | ||
54 | ), | ||
55 | @r###" | ||
56 | [ | ||
57 | CompletionItem { | ||
58 | label: "collections", | ||
59 | source_range: [21; 24), | ||
60 | delete: [21; 24), | ||
61 | insert: "collections", | ||
62 | }, | ||
63 | ] | ||
64 | "### | ||
65 | ); | ||
66 | } | ||
67 | |||
68 | #[test] | ||
31 | fn bind_pat_and_path_ignore_at() { | 69 | fn bind_pat_and_path_ignore_at() { |
32 | assert_debug_snapshot!( | 70 | assert_debug_snapshot!( |
33 | do_reference_completion( | 71 | do_reference_completion( |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 901ad104c..e7e201709 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -1,4 +1,8 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! Collects diagnostics & fixits for a single file. |
2 | //! | ||
3 | //! The tricky bit here is that diagnostics are produced by hir in terms of | ||
4 | //! macro-expanded files, but we need to present them to the users in terms of | ||
5 | //! original files. So we need to map the ranges. | ||
2 | 6 | ||
3 | use std::cell::RefCell; | 7 | use std::cell::RefCell; |
4 | 8 | ||
@@ -46,7 +50,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
46 | let mut sink = DiagnosticSink::new(|d| { | 50 | let mut sink = DiagnosticSink::new(|d| { |
47 | res.borrow_mut().push(Diagnostic { | 51 | res.borrow_mut().push(Diagnostic { |
48 | message: d.message(), | 52 | message: d.message(), |
49 | range: d.highlight_range(), | 53 | range: sema.diagnostics_range(d).range, |
50 | severity: Severity::Error, | 54 | severity: Severity::Error, |
51 | fix: None, | 55 | fix: None, |
52 | }) | 56 | }) |
@@ -62,7 +66,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
62 | let create_file = FileSystemEdit::CreateFile { source_root, path }; | 66 | let create_file = FileSystemEdit::CreateFile { source_root, path }; |
63 | let fix = SourceChange::file_system_edit("create module", create_file); | 67 | let fix = SourceChange::file_system_edit("create module", create_file); |
64 | res.borrow_mut().push(Diagnostic { | 68 | res.borrow_mut().push(Diagnostic { |
65 | range: d.highlight_range(), | 69 | range: sema.diagnostics_range(d).range, |
66 | message: d.message(), | 70 | message: d.message(), |
67 | severity: Severity::Error, | 71 | severity: Severity::Error, |
68 | fix: Some(fix), | 72 | fix: Some(fix), |
@@ -95,7 +99,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
95 | }; | 99 | }; |
96 | 100 | ||
97 | res.borrow_mut().push(Diagnostic { | 101 | res.borrow_mut().push(Diagnostic { |
98 | range: d.highlight_range(), | 102 | range: sema.diagnostics_range(d).range, |
99 | message: d.message(), | 103 | message: d.message(), |
100 | severity: Severity::Error, | 104 | severity: Severity::Error, |
101 | fix, | 105 | fix, |
@@ -103,7 +107,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
103 | }) | 107 | }) |
104 | .on::<hir::diagnostics::MissingMatchArms, _>(|d| { | 108 | .on::<hir::diagnostics::MissingMatchArms, _>(|d| { |
105 | res.borrow_mut().push(Diagnostic { | 109 | res.borrow_mut().push(Diagnostic { |
106 | range: d.highlight_range(), | 110 | range: sema.diagnostics_range(d).range, |
107 | message: d.message(), | 111 | message: d.message(), |
108 | severity: Severity::Error, | 112 | severity: Severity::Error, |
109 | fix: None, | 113 | fix: None, |
@@ -115,7 +119,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
115 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); | 119 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); |
116 | let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit); | 120 | let fix = SourceChange::source_file_edit_from("wrap with ok", file_id, edit); |
117 | res.borrow_mut().push(Diagnostic { | 121 | res.borrow_mut().push(Diagnostic { |
118 | range: d.highlight_range(), | 122 | range: sema.diagnostics_range(d).range, |
119 | message: d.message(), | 123 | message: d.message(), |
120 | severity: Severity::Error, | 124 | severity: Severity::Error, |
121 | fix: Some(fix), | 125 | fix: Some(fix), |
@@ -622,6 +626,62 @@ mod tests { | |||
622 | } | 626 | } |
623 | 627 | ||
624 | #[test] | 628 | #[test] |
629 | fn range_mapping_out_of_macros() { | ||
630 | let (analysis, file_id) = single_file( | ||
631 | r" | ||
632 | fn some() {} | ||
633 | fn items() {} | ||
634 | fn here() {} | ||
635 | |||
636 | macro_rules! id { | ||
637 | ($($tt:tt)*) => { $($tt)*}; | ||
638 | } | ||
639 | |||
640 | fn main() { | ||
641 | let _x = id![Foo { a: 42 }]; | ||
642 | } | ||
643 | |||
644 | pub struct Foo { | ||
645 | pub a: i32, | ||
646 | pub b: i32, | ||
647 | } | ||
648 | ", | ||
649 | ); | ||
650 | let diagnostics = analysis.diagnostics(file_id).unwrap(); | ||
651 | assert_debug_snapshot!(diagnostics, @r###" | ||
652 | [ | ||
653 | Diagnostic { | ||
654 | message: "Missing structure fields:\n- b", | ||
655 | range: [224; 233), | ||
656 | fix: Some( | ||
657 | SourceChange { | ||
658 | label: "fill struct fields", | ||
659 | source_file_edits: [ | ||
660 | SourceFileEdit { | ||
661 | file_id: FileId( | ||
662 | 1, | ||
663 | ), | ||
664 | edit: TextEdit { | ||
665 | atoms: [ | ||
666 | AtomTextEdit { | ||
667 | delete: [3; 9), | ||
668 | insert: "{a:42, b: ()}", | ||
669 | }, | ||
670 | ], | ||
671 | }, | ||
672 | }, | ||
673 | ], | ||
674 | file_system_edits: [], | ||
675 | cursor_position: None, | ||
676 | }, | ||
677 | ), | ||
678 | severity: Error, | ||
679 | }, | ||
680 | ] | ||
681 | "###); | ||
682 | } | ||
683 | |||
684 | #[test] | ||
625 | fn test_check_unnecessary_braces_in_use_statement() { | 685 | fn test_check_unnecessary_braces_in_use_statement() { |
626 | check_not_applicable( | 686 | check_not_applicable( |
627 | " | 687 | " |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 45b9f7802..0774fa0a1 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -237,7 +237,8 @@ fn should_show_param_hint( | |||
237 | ) -> bool { | 237 | ) -> bool { |
238 | if param_name.is_empty() | 238 | if param_name.is_empty() |
239 | || is_argument_similar_to_param(argument, param_name) | 239 | || is_argument_similar_to_param(argument, param_name) |
240 | || Some(param_name) == fn_signature.name.as_ref().map(String::as_str) | 240 | || Some(param_name.trim_start_matches('_')) |
241 | == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) | ||
241 | { | 242 | { |
242 | return false; | 243 | return false; |
243 | } | 244 | } |
@@ -255,6 +256,8 @@ fn should_show_param_hint( | |||
255 | 256 | ||
256 | fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { | 257 | fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { |
257 | let argument_string = remove_ref(argument.clone()).syntax().to_string(); | 258 | let argument_string = remove_ref(argument.clone()).syntax().to_string(); |
259 | let param_name = param_name.trim_start_matches('_'); | ||
260 | let argument_string = argument_string.trim_start_matches('_'); | ||
258 | argument_string.starts_with(¶m_name) || argument_string.ends_with(¶m_name) | 261 | argument_string.starts_with(¶m_name) || argument_string.ends_with(¶m_name) |
259 | } | 262 | } |
260 | 263 | ||
@@ -1094,8 +1097,10 @@ struct Param {} | |||
1094 | 1097 | ||
1095 | fn different_order(param: &Param) {} | 1098 | fn different_order(param: &Param) {} |
1096 | fn different_order_mut(param: &mut Param) {} | 1099 | fn different_order_mut(param: &mut Param) {} |
1100 | fn has_underscore(_param: bool) {} | ||
1097 | 1101 | ||
1098 | fn twiddle(twiddle: bool) {} | 1102 | fn twiddle(twiddle: bool) {} |
1103 | fn doo(_doo: bool) {} | ||
1099 | 1104 | ||
1100 | fn main() { | 1105 | fn main() { |
1101 | let container: TestVarContainer = TestVarContainer { test_var: 42 }; | 1106 | let container: TestVarContainer = TestVarContainer { test_var: 42 }; |
@@ -1112,11 +1117,15 @@ fn main() { | |||
1112 | test_processed.frob(false); | 1117 | test_processed.frob(false); |
1113 | 1118 | ||
1114 | twiddle(true); | 1119 | twiddle(true); |
1120 | doo(true); | ||
1115 | 1121 | ||
1116 | let param_begin: Param = Param {}; | 1122 | let param_begin: Param = Param {}; |
1117 | different_order(¶m_begin); | 1123 | different_order(¶m_begin); |
1118 | different_order(&mut param_begin); | 1124 | different_order(&mut param_begin); |
1119 | 1125 | ||
1126 | let param: bool = true; | ||
1127 | has_underscore(param); | ||
1128 | |||
1120 | let a: f64 = 7.0; | 1129 | let a: f64 = 7.0; |
1121 | let b: f64 = 4.0; | 1130 | let b: f64 = 4.0; |
1122 | let _: f64 = a.div_euclid(b); | 1131 | let _: f64 = a.div_euclid(b); |
diff --git a/crates/ra_ide/src/marks.rs b/crates/ra_ide/src/marks.rs index 5e1f135c5..eee44e886 100644 --- a/crates/ra_ide/src/marks.rs +++ b/crates/ra_ide/src/marks.rs | |||
@@ -8,4 +8,5 @@ test_utils::marks!( | |||
8 | test_resolve_parent_module_on_module_decl | 8 | test_resolve_parent_module_on_module_decl |
9 | search_filters_by_range | 9 | search_filters_by_range |
10 | dont_insert_macro_call_parens_unncessary | 10 | dont_insert_macro_call_parens_unncessary |
11 | self_fulfilling_completion | ||
11 | ); | 12 | ); |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 83d161f45..7b15b82bd 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -174,7 +174,8 @@ pub(crate) fn highlight( | |||
174 | } | 174 | } |
175 | 175 | ||
176 | assert_eq!(res.len(), 1, "after DFS traversal, the stack should only contain a single element"); | 176 | assert_eq!(res.len(), 1, "after DFS traversal, the stack should only contain a single element"); |
177 | let res = res.pop().unwrap(); | 177 | let mut res = res.pop().unwrap(); |
178 | res.sort_by_key(|range| range.range.start()); | ||
178 | // Check that ranges are sorted and disjoint | 179 | // Check that ranges are sorted and disjoint |
179 | assert!(res | 180 | assert!(res |
180 | .iter() | 181 | .iter() |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 110887c2a..73611e23a 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -156,3 +156,15 @@ fn main() { | |||
156 | fs::write(dst_file, &actual_html).unwrap(); | 156 | fs::write(dst_file, &actual_html).unwrap(); |
157 | assert_eq_text!(expected_html, actual_html); | 157 | assert_eq_text!(expected_html, actual_html); |
158 | } | 158 | } |
159 | |||
160 | #[test] | ||
161 | fn ranges_sorted() { | ||
162 | let (analysis, file_id) = single_file( | ||
163 | r#" | ||
164 | #[foo(bar = "bar")] | ||
165 | macro_rules! test {} | ||
166 | }"# | ||
167 | .trim(), | ||
168 | ); | ||
169 | let _ = analysis.highlight(file_id).unwrap(); | ||
170 | } | ||