diff options
Diffstat (limited to 'crates/ra_ide')
21 files changed, 478 insertions, 255 deletions
diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index 85d1f0cb1..defd8176f 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs | |||
@@ -246,6 +246,35 @@ mod tests { | |||
246 | } | 246 | } |
247 | 247 | ||
248 | #[test] | 248 | #[test] |
249 | fn test_call_hierarchy_in_tests_mod() { | ||
250 | check_hierarchy( | ||
251 | r#" | ||
252 | //- /lib.rs cfg:test | ||
253 | fn callee() {} | ||
254 | fn caller1() { | ||
255 | call<|>ee(); | ||
256 | } | ||
257 | |||
258 | #[cfg(test)] | ||
259 | mod tests { | ||
260 | use super::*; | ||
261 | |||
262 | #[test] | ||
263 | fn test_caller() { | ||
264 | callee(); | ||
265 | } | ||
266 | } | ||
267 | "#, | ||
268 | "callee FN_DEF FileId(1) 0..14 3..9", | ||
269 | &[ | ||
270 | "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]", | ||
271 | "test_caller FN_DEF FileId(1) 93..147 108..119 : [132..138]", | ||
272 | ], | ||
273 | &[], | ||
274 | ); | ||
275 | } | ||
276 | |||
277 | #[test] | ||
249 | fn test_call_hierarchy_in_different_files() { | 278 | fn test_call_hierarchy_in_different_files() { |
250 | check_hierarchy( | 279 | check_hierarchy( |
251 | r#" | 280 | r#" |
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index 6021f7279..cfb7c1e38 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs | |||
@@ -63,8 +63,8 @@ impl fmt::Debug for CompletionItem { | |||
63 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 63 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
64 | let mut s = f.debug_struct("CompletionItem"); | 64 | let mut s = f.debug_struct("CompletionItem"); |
65 | s.field("label", &self.label()).field("source_range", &self.source_range()); | 65 | s.field("label", &self.label()).field("source_range", &self.source_range()); |
66 | if self.text_edit().as_indels().len() == 1 { | 66 | if self.text_edit().len() == 1 { |
67 | let atom = &self.text_edit().as_indels()[0]; | 67 | let atom = &self.text_edit().iter().next().unwrap(); |
68 | s.field("delete", &atom.delete); | 68 | s.field("delete", &atom.delete); |
69 | s.field("insert", &atom.insert); | 69 | s.field("insert", &atom.insert); |
70 | } else { | 70 | } else { |
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 54c2bcc09..3d83c0f71 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs | |||
@@ -21,7 +21,7 @@ use ra_syntax::{ | |||
21 | }; | 21 | }; |
22 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 22 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
23 | 23 | ||
24 | use crate::{Diagnostic, FileId, FileSystemEdit, SourceChange, SourceFileEdit}; | 24 | use crate::{Diagnostic, FileId, FileSystemEdit, Fix, SourceChange, SourceFileEdit}; |
25 | 25 | ||
26 | #[derive(Debug, Copy, Clone)] | 26 | #[derive(Debug, Copy, Clone)] |
27 | pub enum Severity { | 27 | pub enum Severity { |
@@ -63,8 +63,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
63 | .parent() | 63 | .parent() |
64 | .unwrap_or_else(|| RelativePath::new("")) | 64 | .unwrap_or_else(|| RelativePath::new("")) |
65 | .join(&d.candidate); | 65 | .join(&d.candidate); |
66 | let create_file = FileSystemEdit::CreateFile { source_root, path }; | 66 | let fix = |
67 | let fix = SourceChange::file_system_edit("Create module", create_file); | 67 | Fix::new("Create module", FileSystemEdit::CreateFile { source_root, path }.into()); |
68 | res.borrow_mut().push(Diagnostic { | 68 | res.borrow_mut().push(Diagnostic { |
69 | range: sema.diagnostics_range(d).range, | 69 | range: sema.diagnostics_range(d).range, |
70 | message: d.message(), | 70 | message: d.message(), |
@@ -88,14 +88,12 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
88 | field_list = field_list.append_field(&field); | 88 | field_list = field_list.append_field(&field); |
89 | } | 89 | } |
90 | 90 | ||
91 | let mut builder = TextEditBuilder::default(); | 91 | let edit = { |
92 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); | 92 | let mut builder = TextEditBuilder::default(); |
93 | 93 | algo::diff(&d.ast(db).syntax(), &field_list.syntax()).into_text_edit(&mut builder); | |
94 | Some(SourceChange::source_file_edit_from( | 94 | builder.finish() |
95 | "Fill struct fields", | 95 | }; |
96 | file_id, | 96 | Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into())) |
97 | builder.finish(), | ||
98 | )) | ||
99 | }; | 97 | }; |
100 | 98 | ||
101 | res.borrow_mut().push(Diagnostic { | 99 | res.borrow_mut().push(Diagnostic { |
@@ -117,7 +115,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic> | |||
117 | let node = d.ast(db); | 115 | let node = d.ast(db); |
118 | let replacement = format!("Ok({})", node.syntax()); | 116 | let replacement = format!("Ok({})", node.syntax()); |
119 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); | 117 | let edit = TextEdit::replace(node.syntax().text_range(), replacement); |
120 | let fix = SourceChange::source_file_edit_from("Wrap with ok", file_id, edit); | 118 | let source_change = SourceChange::source_file_edit_from(file_id, edit); |
119 | let fix = Fix::new("Wrap with ok", source_change); | ||
121 | res.borrow_mut().push(Diagnostic { | 120 | res.borrow_mut().push(Diagnostic { |
122 | range: sema.diagnostics_range(d).range, | 121 | range: sema.diagnostics_range(d).range, |
123 | message: d.message(), | 122 | message: d.message(), |
@@ -154,9 +153,9 @@ fn check_unnecessary_braces_in_use_statement( | |||
154 | range, | 153 | range, |
155 | message: "Unnecessary braces in use statement".to_string(), | 154 | message: "Unnecessary braces in use statement".to_string(), |
156 | severity: Severity::WeakWarning, | 155 | severity: Severity::WeakWarning, |
157 | fix: Some(SourceChange::source_file_edit( | 156 | fix: Some(Fix::new( |
158 | "Remove unnecessary braces", | 157 | "Remove unnecessary braces", |
159 | SourceFileEdit { file_id, edit }, | 158 | SourceFileEdit { file_id, edit }.into(), |
160 | )), | 159 | )), |
161 | }); | 160 | }); |
162 | } | 161 | } |
@@ -198,9 +197,9 @@ fn check_struct_shorthand_initialization( | |||
198 | range: record_field.syntax().text_range(), | 197 | range: record_field.syntax().text_range(), |
199 | message: "Shorthand struct initialization".to_string(), | 198 | message: "Shorthand struct initialization".to_string(), |
200 | severity: Severity::WeakWarning, | 199 | severity: Severity::WeakWarning, |
201 | fix: Some(SourceChange::source_file_edit( | 200 | fix: Some(Fix::new( |
202 | "Use struct shorthand initialization", | 201 | "Use struct shorthand initialization", |
203 | SourceFileEdit { file_id, edit }, | 202 | SourceFileEdit { file_id, edit }.into(), |
204 | )), | 203 | )), |
205 | }); | 204 | }); |
206 | } | 205 | } |
@@ -240,7 +239,7 @@ mod tests { | |||
240 | let diagnostic = | 239 | let diagnostic = |
241 | diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); | 240 | diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before)); |
242 | let mut fix = diagnostic.fix.unwrap(); | 241 | let mut fix = diagnostic.fix.unwrap(); |
243 | let edit = fix.source_file_edits.pop().unwrap().edit; | 242 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; |
244 | let actual = { | 243 | let actual = { |
245 | let mut actual = before.to_string(); | 244 | let mut actual = before.to_string(); |
246 | edit.apply(&mut actual); | 245 | edit.apply(&mut actual); |
@@ -258,7 +257,7 @@ mod tests { | |||
258 | let (analysis, file_position) = analysis_and_position(fixture); | 257 | let (analysis, file_position) = analysis_and_position(fixture); |
259 | let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); | 258 | let diagnostic = analysis.diagnostics(file_position.file_id).unwrap().pop().unwrap(); |
260 | let mut fix = diagnostic.fix.unwrap(); | 259 | let mut fix = diagnostic.fix.unwrap(); |
261 | let edit = fix.source_file_edits.pop().unwrap().edit; | 260 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; |
262 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); | 261 | let target_file_contents = analysis.file_text(file_position.file_id).unwrap(); |
263 | let actual = { | 262 | let actual = { |
264 | let mut actual = target_file_contents.to_string(); | 263 | let mut actual = target_file_contents.to_string(); |
@@ -295,7 +294,7 @@ mod tests { | |||
295 | let (analysis, file_id) = single_file(before); | 294 | let (analysis, file_id) = single_file(before); |
296 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); | 295 | let diagnostic = analysis.diagnostics(file_id).unwrap().pop().unwrap(); |
297 | let mut fix = diagnostic.fix.unwrap(); | 296 | let mut fix = diagnostic.fix.unwrap(); |
298 | let edit = fix.source_file_edits.pop().unwrap().edit; | 297 | let edit = fix.source_change.source_file_edits.pop().unwrap().edit; |
299 | let actual = { | 298 | let actual = { |
300 | let mut actual = before.to_string(); | 299 | let mut actual = before.to_string(); |
301 | edit.apply(&mut actual); | 300 | edit.apply(&mut actual); |
@@ -616,23 +615,24 @@ mod tests { | |||
616 | Diagnostic { | 615 | Diagnostic { |
617 | message: "unresolved module", | 616 | message: "unresolved module", |
618 | range: 0..8, | 617 | range: 0..8, |
618 | severity: Error, | ||
619 | fix: Some( | 619 | fix: Some( |
620 | SourceChange { | 620 | Fix { |
621 | label: "Create module", | 621 | label: "Create module", |
622 | source_file_edits: [], | 622 | source_change: SourceChange { |
623 | file_system_edits: [ | 623 | source_file_edits: [], |
624 | CreateFile { | 624 | file_system_edits: [ |
625 | source_root: SourceRootId( | 625 | CreateFile { |
626 | 0, | 626 | source_root: SourceRootId( |
627 | ), | 627 | 0, |
628 | path: "foo.rs", | 628 | ), |
629 | }, | 629 | path: "foo.rs", |
630 | ], | 630 | }, |
631 | cursor_position: None, | 631 | ], |
632 | is_snippet: false, | 632 | is_snippet: false, |
633 | }, | ||
633 | }, | 634 | }, |
634 | ), | 635 | ), |
635 | severity: Error, | ||
636 | }, | 636 | }, |
637 | ] | 637 | ] |
638 | "###); | 638 | "###); |
@@ -666,30 +666,31 @@ mod tests { | |||
666 | Diagnostic { | 666 | Diagnostic { |
667 | message: "Missing structure fields:\n- b", | 667 | message: "Missing structure fields:\n- b", |
668 | range: 224..233, | 668 | range: 224..233, |
669 | severity: Error, | ||
669 | fix: Some( | 670 | fix: Some( |
670 | SourceChange { | 671 | Fix { |
671 | label: "Fill struct fields", | 672 | label: "Fill struct fields", |
672 | source_file_edits: [ | 673 | source_change: SourceChange { |
673 | SourceFileEdit { | 674 | source_file_edits: [ |
674 | file_id: FileId( | 675 | SourceFileEdit { |
675 | 1, | 676 | file_id: FileId( |
676 | ), | 677 | 1, |
677 | edit: TextEdit { | 678 | ), |
678 | indels: [ | 679 | edit: TextEdit { |
679 | Indel { | 680 | indels: [ |
680 | insert: "{a:42, b: ()}", | 681 | Indel { |
681 | delete: 3..9, | 682 | insert: "{a:42, b: ()}", |
682 | }, | 683 | delete: 3..9, |
683 | ], | 684 | }, |
685 | ], | ||
686 | }, | ||
684 | }, | 687 | }, |
685 | }, | 688 | ], |
686 | ], | 689 | file_system_edits: [], |
687 | file_system_edits: [], | 690 | is_snippet: false, |
688 | cursor_position: None, | 691 | }, |
689 | is_snippet: false, | ||
690 | }, | 692 | }, |
691 | ), | 693 | ), |
692 | severity: Error, | ||
693 | }, | 694 | }, |
694 | ] | 695 | ] |
695 | "###); | 696 | "###); |
diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs index 722092de9..827c094e7 100644 --- a/crates/ra_ide/src/display.rs +++ b/crates/ra_ide/src/display.rs | |||
@@ -79,16 +79,17 @@ pub(crate) fn rust_code_markup_with_doc( | |||
79 | doc: Option<&str>, | 79 | doc: Option<&str>, |
80 | mod_path: Option<&str>, | 80 | mod_path: Option<&str>, |
81 | ) -> String { | 81 | ) -> String { |
82 | let mut buf = "```rust\n".to_owned(); | 82 | let mut buf = String::new(); |
83 | 83 | ||
84 | if let Some(mod_path) = mod_path { | 84 | if let Some(mod_path) = mod_path { |
85 | if !mod_path.is_empty() { | 85 | if !mod_path.is_empty() { |
86 | format_to!(buf, "{}\n", mod_path); | 86 | format_to!(buf, "```rust\n{}\n```\n\n", mod_path); |
87 | } | 87 | } |
88 | } | 88 | } |
89 | format_to!(buf, "{}\n```", code); | 89 | format_to!(buf, "```rust\n{}\n```", code); |
90 | 90 | ||
91 | if let Some(doc) = doc { | 91 | if let Some(doc) = doc { |
92 | format_to!(buf, "\n___"); | ||
92 | format_to!(buf, "\n\n{}", doc); | 93 | format_to!(buf, "\n\n{}", doc); |
93 | } | 94 | } |
94 | 95 | ||
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index befa977c7..3e721dcca 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -405,7 +405,7 @@ mod tests { | |||
405 | }; | 405 | }; |
406 | } | 406 | } |
407 | "#, | 407 | "#, |
408 | &["Foo\nfield_a: u32"], | 408 | &["Foo\n```\n\n```rust\nfield_a: u32"], |
409 | ); | 409 | ); |
410 | 410 | ||
411 | // Hovering over the field in the definition | 411 | // Hovering over the field in the definition |
@@ -422,7 +422,7 @@ mod tests { | |||
422 | }; | 422 | }; |
423 | } | 423 | } |
424 | "#, | 424 | "#, |
425 | &["Foo\nfield_a: u32"], | 425 | &["Foo\n```\n\n```rust\nfield_a: u32"], |
426 | ); | 426 | ); |
427 | } | 427 | } |
428 | 428 | ||
@@ -475,7 +475,7 @@ fn main() { | |||
475 | ", | 475 | ", |
476 | ); | 476 | ); |
477 | let hover = analysis.hover(position).unwrap().unwrap(); | 477 | let hover = analysis.hover(position).unwrap().unwrap(); |
478 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome")); | 478 | assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n```\n\n```rust\nSome")); |
479 | 479 | ||
480 | let (analysis, position) = single_file_with_position( | 480 | let (analysis, position) = single_file_with_position( |
481 | " | 481 | " |
@@ -503,8 +503,12 @@ fn main() { | |||
503 | "#, | 503 | "#, |
504 | &[" | 504 | &[" |
505 | Option | 505 | Option |
506 | ``` | ||
507 | |||
508 | ```rust | ||
506 | None | 509 | None |
507 | ``` | 510 | ``` |
511 | ___ | ||
508 | 512 | ||
509 | The None variant | 513 | The None variant |
510 | " | 514 | " |
@@ -524,8 +528,12 @@ The None variant | |||
524 | "#, | 528 | "#, |
525 | &[" | 529 | &[" |
526 | Option | 530 | Option |
531 | ``` | ||
532 | |||
533 | ```rust | ||
527 | Some | 534 | Some |
528 | ``` | 535 | ``` |
536 | ___ | ||
529 | 537 | ||
530 | The Some variant | 538 | The Some variant |
531 | " | 539 | " |
@@ -606,7 +614,10 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
606 | ", | 614 | ", |
607 | ); | 615 | ); |
608 | let hover = analysis.hover(position).unwrap().unwrap(); | 616 | let hover = analysis.hover(position).unwrap().unwrap(); |
609 | assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing")); | 617 | assert_eq!( |
618 | trim_markup_opt(hover.info.first()), | ||
619 | Some("wrapper::Thing\n```\n\n```rust\nfn new() -> Thing") | ||
620 | ); | ||
610 | } | 621 | } |
611 | 622 | ||
612 | #[test] | 623 | #[test] |
@@ -882,7 +893,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
882 | fo<|>o(); | 893 | fo<|>o(); |
883 | } | 894 | } |
884 | ", | 895 | ", |
885 | &["fn foo()\n```\n\n<- `\u{3000}` here"], | 896 | &["fn foo()\n```\n___\n\n<- `\u{3000}` here"], |
886 | ); | 897 | ); |
887 | } | 898 | } |
888 | 899 | ||
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs index d3af780c4..af1ade8a1 100644 --- a/crates/ra_ide/src/join_lines.rs +++ b/crates/ra_ide/src/join_lines.rs | |||
@@ -166,16 +166,28 @@ fn is_trailing_comma(left: SyntaxKind, right: SyntaxKind) -> bool { | |||
166 | 166 | ||
167 | #[cfg(test)] | 167 | #[cfg(test)] |
168 | mod tests { | 168 | mod tests { |
169 | use crate::test_utils::{assert_eq_text, check_action, extract_range}; | 169 | use ra_syntax::SourceFile; |
170 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; | ||
170 | 171 | ||
171 | use super::*; | 172 | use super::*; |
172 | 173 | ||
173 | fn check_join_lines(before: &str, after: &str) { | 174 | fn check_join_lines(before: &str, after: &str) { |
174 | check_action(before, after, |file, offset| { | 175 | let (before_cursor_pos, before) = extract_offset(before); |
175 | let range = TextRange::empty(offset); | 176 | let file = SourceFile::parse(&before).ok().unwrap(); |
176 | let res = join_lines(file, range); | 177 | |
177 | Some(res) | 178 | let range = TextRange::empty(before_cursor_pos); |
178 | }) | 179 | let result = join_lines(&file, range); |
180 | |||
181 | let actual = { | ||
182 | let mut actual = before.to_string(); | ||
183 | result.apply(&mut actual); | ||
184 | actual | ||
185 | }; | ||
186 | let actual_cursor_pos = result | ||
187 | .apply_to_offset(before_cursor_pos) | ||
188 | .expect("cursor position is affected by the edit"); | ||
189 | let actual = add_cursor(&actual, actual_cursor_pos); | ||
190 | assert_eq_text!(after, &actual); | ||
179 | } | 191 | } |
180 | 192 | ||
181 | #[test] | 193 | #[test] |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 83cb498f7..d983cd910 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -42,9 +42,6 @@ mod inlay_hints; | |||
42 | mod expand_macro; | 42 | mod expand_macro; |
43 | mod ssr; | 43 | mod ssr; |
44 | 44 | ||
45 | #[cfg(test)] | ||
46 | mod test_utils; | ||
47 | |||
48 | use std::sync::Arc; | 45 | use std::sync::Arc; |
49 | 46 | ||
50 | use ra_cfg::CfgOptions; | 47 | use ra_cfg::CfgOptions; |
@@ -87,12 +84,12 @@ pub use ra_db::{ | |||
87 | pub use ra_ide_db::{ | 84 | pub use ra_ide_db::{ |
88 | change::{AnalysisChange, LibraryData}, | 85 | change::{AnalysisChange, LibraryData}, |
89 | line_index::{LineCol, LineIndex}, | 86 | line_index::{LineCol, LineIndex}, |
90 | line_index_utils::translate_offset_with_edit, | ||
91 | search::SearchScope, | 87 | search::SearchScope, |
92 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, | 88 | source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, |
93 | symbol_index::Query, | 89 | symbol_index::Query, |
94 | RootDatabase, | 90 | RootDatabase, |
95 | }; | 91 | }; |
92 | pub use ra_text_edit::{Indel, TextEdit}; | ||
96 | 93 | ||
97 | pub type Cancelable<T> = Result<T, Canceled>; | 94 | pub type Cancelable<T> = Result<T, Canceled>; |
98 | 95 | ||
@@ -100,8 +97,22 @@ pub type Cancelable<T> = Result<T, Canceled>; | |||
100 | pub struct Diagnostic { | 97 | pub struct Diagnostic { |
101 | pub message: String, | 98 | pub message: String, |
102 | pub range: TextRange, | 99 | pub range: TextRange, |
103 | pub fix: Option<SourceChange>, | ||
104 | pub severity: Severity, | 100 | pub severity: Severity, |
101 | pub fix: Option<Fix>, | ||
102 | } | ||
103 | |||
104 | #[derive(Debug)] | ||
105 | pub struct Fix { | ||
106 | pub label: String, | ||
107 | pub source_change: SourceChange, | ||
108 | } | ||
109 | |||
110 | impl Fix { | ||
111 | pub fn new(label: impl Into<String>, source_change: SourceChange) -> Self { | ||
112 | let label = label.into(); | ||
113 | assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.')); | ||
114 | Self { label, source_change } | ||
115 | } | ||
105 | } | 116 | } |
106 | 117 | ||
107 | /// Info associated with a text range. | 118 | /// Info associated with a text range. |
@@ -289,20 +300,17 @@ impl Analysis { | |||
289 | 300 | ||
290 | /// Returns an edit to remove all newlines in the range, cleaning up minor | 301 | /// Returns an edit to remove all newlines in the range, cleaning up minor |
291 | /// stuff like trailing commas. | 302 | /// stuff like trailing commas. |
292 | pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { | 303 | pub fn join_lines(&self, frange: FileRange) -> Cancelable<TextEdit> { |
293 | self.with_db(|db| { | 304 | self.with_db(|db| { |
294 | let parse = db.parse(frange.file_id); | 305 | let parse = db.parse(frange.file_id); |
295 | let file_edit = SourceFileEdit { | 306 | join_lines::join_lines(&parse.tree(), frange.range) |
296 | file_id: frange.file_id, | ||
297 | edit: join_lines::join_lines(&parse.tree(), frange.range), | ||
298 | }; | ||
299 | SourceChange::source_file_edit("Join lines", file_edit) | ||
300 | }) | 307 | }) |
301 | } | 308 | } |
302 | 309 | ||
303 | /// Returns an edit which should be applied when opening a new line, fixing | 310 | /// Returns an edit which should be applied when opening a new line, fixing |
304 | /// up minor stuff like continuing the comment. | 311 | /// up minor stuff like continuing the comment. |
305 | pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<SourceChange>> { | 312 | /// The edit will be a snippet (with `$0`). |
313 | pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<TextEdit>> { | ||
306 | self.with_db(|db| typing::on_enter(&db, position)) | 314 | self.with_db(|db| typing::on_enter(&db, position)) |
307 | } | 315 | } |
308 | 316 | ||
@@ -500,7 +508,7 @@ impl Analysis { | |||
500 | ) -> Cancelable<Result<SourceChange, SsrError>> { | 508 | ) -> Cancelable<Result<SourceChange, SsrError>> { |
501 | self.with_db(|db| { | 509 | self.with_db(|db| { |
502 | let edits = ssr::parse_search_replace(query, parse_only, db)?; | 510 | let edits = ssr::parse_search_replace(query, parse_only, db)?; |
503 | Ok(SourceChange::source_file_edits("Structural Search Replace", edits)) | 511 | Ok(SourceChange::source_file_edits(edits)) |
504 | }) | 512 | }) |
505 | } | 513 | } |
506 | 514 | ||
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 2c13f206a..ad78d2d93 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs | |||
@@ -1,21 +1,81 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use std::str::FromStr; | ||
3 | use std::sync::Arc; | 4 | use std::sync::Arc; |
4 | 5 | ||
5 | use ra_cfg::CfgOptions; | 6 | use ra_cfg::CfgOptions; |
6 | use ra_db::{CrateName, Env, RelativePathBuf}; | 7 | use ra_db::{CrateName, Env, RelativePathBuf}; |
7 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; | 8 | use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER}; |
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition::Edition2018, FileId, FilePosition, | 11 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, |
11 | FileRange, SourceRootId, | 12 | SourceRootId, |
12 | }; | 13 | }; |
13 | 14 | ||
15 | #[derive(Debug)] | ||
16 | enum MockFileData { | ||
17 | Plain { path: String, content: String }, | ||
18 | Fixture(FixtureEntry), | ||
19 | } | ||
20 | |||
21 | impl MockFileData { | ||
22 | fn new(path: String, content: String) -> Self { | ||
23 | // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` ' | ||
24 | // see https://github.com/rust-lang/rust/issues/69018 | ||
25 | MockFileData::Plain { path, content } | ||
26 | } | ||
27 | |||
28 | fn path(&self) -> &str { | ||
29 | match self { | ||
30 | MockFileData::Plain { path, .. } => path.as_str(), | ||
31 | MockFileData::Fixture(f) => f.meta.path().as_str(), | ||
32 | } | ||
33 | } | ||
34 | |||
35 | fn content(&self) -> &str { | ||
36 | match self { | ||
37 | MockFileData::Plain { content, .. } => content, | ||
38 | MockFileData::Fixture(f) => f.text.as_str(), | ||
39 | } | ||
40 | } | ||
41 | |||
42 | fn cfg_options(&self) -> CfgOptions { | ||
43 | match self { | ||
44 | MockFileData::Fixture(f) => { | ||
45 | f.meta.cfg_options().map_or_else(Default::default, |o| o.clone()) | ||
46 | } | ||
47 | _ => CfgOptions::default(), | ||
48 | } | ||
49 | } | ||
50 | |||
51 | fn edition(&self) -> Edition { | ||
52 | match self { | ||
53 | MockFileData::Fixture(f) => { | ||
54 | f.meta.edition().map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap()) | ||
55 | } | ||
56 | _ => Edition::Edition2018, | ||
57 | } | ||
58 | } | ||
59 | |||
60 | fn env(&self) -> Env { | ||
61 | match self { | ||
62 | MockFileData::Fixture(f) => Env::from(f.meta.env()), | ||
63 | _ => Env::default(), | ||
64 | } | ||
65 | } | ||
66 | } | ||
67 | |||
68 | impl From<FixtureEntry> for MockFileData { | ||
69 | fn from(fixture: FixtureEntry) -> Self { | ||
70 | Self::Fixture(fixture) | ||
71 | } | ||
72 | } | ||
73 | |||
14 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis | 74 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis |
15 | /// from a set of in-memory files. | 75 | /// from a set of in-memory files. |
16 | #[derive(Debug, Default)] | 76 | #[derive(Debug, Default)] |
17 | pub struct MockAnalysis { | 77 | pub struct MockAnalysis { |
18 | files: Vec<(String, String)>, | 78 | files: Vec<MockFileData>, |
19 | } | 79 | } |
20 | 80 | ||
21 | impl MockAnalysis { | 81 | impl MockAnalysis { |
@@ -35,7 +95,7 @@ impl MockAnalysis { | |||
35 | pub fn with_files(fixture: &str) -> MockAnalysis { | 95 | pub fn with_files(fixture: &str) -> MockAnalysis { |
36 | let mut res = MockAnalysis::new(); | 96 | let mut res = MockAnalysis::new(); |
37 | for entry in parse_fixture(fixture) { | 97 | for entry in parse_fixture(fixture) { |
38 | res.add_file(&entry.meta, &entry.text); | 98 | res.add_file_fixture(entry); |
39 | } | 99 | } |
40 | res | 100 | res |
41 | } | 101 | } |
@@ -48,30 +108,44 @@ impl MockAnalysis { | |||
48 | for entry in parse_fixture(fixture) { | 108 | for entry in parse_fixture(fixture) { |
49 | if entry.text.contains(CURSOR_MARKER) { | 109 | if entry.text.contains(CURSOR_MARKER) { |
50 | assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); | 110 | assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); |
51 | position = Some(res.add_file_with_position(&entry.meta, &entry.text)); | 111 | position = Some(res.add_file_fixture_with_position(entry)); |
52 | } else { | 112 | } else { |
53 | res.add_file(&entry.meta, &entry.text); | 113 | res.add_file_fixture(entry); |
54 | } | 114 | } |
55 | } | 115 | } |
56 | let position = position.expect("expected a marker (<|>)"); | 116 | let position = position.expect("expected a marker (<|>)"); |
57 | (res, position) | 117 | (res, position) |
58 | } | 118 | } |
59 | 119 | ||
120 | pub fn add_file_fixture(&mut self, fixture: FixtureEntry) -> FileId { | ||
121 | let file_id = self.next_id(); | ||
122 | self.files.push(MockFileData::from(fixture)); | ||
123 | file_id | ||
124 | } | ||
125 | |||
126 | pub fn add_file_fixture_with_position(&mut self, mut fixture: FixtureEntry) -> FilePosition { | ||
127 | let (offset, text) = extract_offset(&fixture.text); | ||
128 | fixture.text = text; | ||
129 | let file_id = self.next_id(); | ||
130 | self.files.push(MockFileData::from(fixture)); | ||
131 | FilePosition { file_id, offset } | ||
132 | } | ||
133 | |||
60 | pub fn add_file(&mut self, path: &str, text: &str) -> FileId { | 134 | pub fn add_file(&mut self, path: &str, text: &str) -> FileId { |
61 | let file_id = FileId((self.files.len() + 1) as u32); | 135 | let file_id = self.next_id(); |
62 | self.files.push((path.to_string(), text.to_string())); | 136 | self.files.push(MockFileData::new(path.to_string(), text.to_string())); |
63 | file_id | 137 | file_id |
64 | } | 138 | } |
65 | pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { | 139 | pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { |
66 | let (offset, text) = extract_offset(text); | 140 | let (offset, text) = extract_offset(text); |
67 | let file_id = FileId((self.files.len() + 1) as u32); | 141 | let file_id = self.next_id(); |
68 | self.files.push((path.to_string(), text)); | 142 | self.files.push(MockFileData::new(path.to_string(), text)); |
69 | FilePosition { file_id, offset } | 143 | FilePosition { file_id, offset } |
70 | } | 144 | } |
71 | pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { | 145 | pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { |
72 | let (range, text) = extract_range(text); | 146 | let (range, text) = extract_range(text); |
73 | let file_id = FileId((self.files.len() + 1) as u32); | 147 | let file_id = self.next_id(); |
74 | self.files.push((path.to_string(), text)); | 148 | self.files.push(MockFileData::new(path.to_string(), text)); |
75 | FileRange { file_id, range } | 149 | FileRange { file_id, range } |
76 | } | 150 | } |
77 | pub fn id_of(&self, path: &str) -> FileId { | 151 | pub fn id_of(&self, path: &str) -> FileId { |
@@ -79,7 +153,7 @@ impl MockAnalysis { | |||
79 | .files | 153 | .files |
80 | .iter() | 154 | .iter() |
81 | .enumerate() | 155 | .enumerate() |
82 | .find(|(_, (p, _text))| path == p) | 156 | .find(|(_, data)| path == data.path()) |
83 | .expect("no file in this mock"); | 157 | .expect("no file in this mock"); |
84 | FileId(idx as u32 + 1) | 158 | FileId(idx as u32 + 1) |
85 | } | 159 | } |
@@ -90,18 +164,21 @@ impl MockAnalysis { | |||
90 | change.add_root(source_root, true); | 164 | change.add_root(source_root, true); |
91 | let mut crate_graph = CrateGraph::default(); | 165 | let mut crate_graph = CrateGraph::default(); |
92 | let mut root_crate = None; | 166 | let mut root_crate = None; |
93 | for (i, (path, contents)) in self.files.into_iter().enumerate() { | 167 | for (i, data) in self.files.into_iter().enumerate() { |
168 | let path = data.path(); | ||
94 | assert!(path.starts_with('/')); | 169 | assert!(path.starts_with('/')); |
95 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); | 170 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); |
171 | let cfg_options = data.cfg_options(); | ||
96 | let file_id = FileId(i as u32 + 1); | 172 | let file_id = FileId(i as u32 + 1); |
97 | let cfg_options = CfgOptions::default(); | 173 | let edition = data.edition(); |
174 | let env = data.env(); | ||
98 | if path == "/lib.rs" || path == "/main.rs" { | 175 | if path == "/lib.rs" || path == "/main.rs" { |
99 | root_crate = Some(crate_graph.add_crate_root( | 176 | root_crate = Some(crate_graph.add_crate_root( |
100 | file_id, | 177 | file_id, |
101 | Edition2018, | 178 | edition, |
102 | None, | 179 | None, |
103 | cfg_options, | 180 | cfg_options, |
104 | Env::default(), | 181 | env, |
105 | Default::default(), | 182 | Default::default(), |
106 | Default::default(), | 183 | Default::default(), |
107 | )); | 184 | )); |
@@ -109,10 +186,10 @@ impl MockAnalysis { | |||
109 | let crate_name = path.parent().unwrap().file_name().unwrap(); | 186 | let crate_name = path.parent().unwrap().file_name().unwrap(); |
110 | let other_crate = crate_graph.add_crate_root( | 187 | let other_crate = crate_graph.add_crate_root( |
111 | file_id, | 188 | file_id, |
112 | Edition2018, | 189 | edition, |
113 | Some(CrateName::new(crate_name).unwrap()), | 190 | Some(CrateName::new(crate_name).unwrap()), |
114 | cfg_options, | 191 | cfg_options, |
115 | Env::default(), | 192 | env, |
116 | Default::default(), | 193 | Default::default(), |
117 | Default::default(), | 194 | Default::default(), |
118 | ); | 195 | ); |
@@ -122,7 +199,7 @@ impl MockAnalysis { | |||
122 | .unwrap(); | 199 | .unwrap(); |
123 | } | 200 | } |
124 | } | 201 | } |
125 | change.add_file(source_root, file_id, path, Arc::new(contents)); | 202 | change.add_file(source_root, file_id, path, Arc::new(data.content().to_owned())); |
126 | } | 203 | } |
127 | change.set_crate_graph(crate_graph); | 204 | change.set_crate_graph(crate_graph); |
128 | host.apply_change(change); | 205 | host.apply_change(change); |
@@ -131,6 +208,10 @@ impl MockAnalysis { | |||
131 | pub fn analysis(self) -> Analysis { | 208 | pub fn analysis(self) -> Analysis { |
132 | self.analysis_host().analysis() | 209 | self.analysis_host().analysis() |
133 | } | 210 | } |
211 | |||
212 | fn next_id(&self) -> FileId { | ||
213 | FileId((self.files.len() + 1) as u32) | ||
214 | } | ||
134 | } | 215 | } |
135 | 216 | ||
136 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. | 217 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. |
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 62ec8d85d..28c6349b1 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs | |||
@@ -128,7 +128,7 @@ fn rename_mod( | |||
128 | source_file_edits.extend(ref_edits); | 128 | source_file_edits.extend(ref_edits); |
129 | } | 129 | } |
130 | 130 | ||
131 | Some(SourceChange::from_edits("Rename", source_file_edits, file_system_edits)) | 131 | Some(SourceChange::from_edits(source_file_edits, file_system_edits)) |
132 | } | 132 | } |
133 | 133 | ||
134 | fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { | 134 | fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { |
@@ -171,7 +171,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo | |||
171 | ), | 171 | ), |
172 | }); | 172 | }); |
173 | 173 | ||
174 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) | 174 | Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) |
175 | } | 175 | } |
176 | 176 | ||
177 | fn text_edit_from_self_param( | 177 | fn text_edit_from_self_param( |
@@ -234,7 +234,7 @@ fn rename_self_to_param( | |||
234 | let range = ast::SelfParam::cast(self_token.parent()) | 234 | let range = ast::SelfParam::cast(self_token.parent()) |
235 | .map_or(self_token.text_range(), |p| p.syntax().text_range()); | 235 | .map_or(self_token.text_range(), |p| p.syntax().text_range()); |
236 | 236 | ||
237 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edits))) | 237 | Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) |
238 | } | 238 | } |
239 | 239 | ||
240 | fn rename_reference( | 240 | fn rename_reference( |
@@ -253,7 +253,7 @@ fn rename_reference( | |||
253 | return None; | 253 | return None; |
254 | } | 254 | } |
255 | 255 | ||
256 | Some(RangeInfo::new(range, SourceChange::source_file_edits("Rename", edit))) | 256 | Some(RangeInfo::new(range, SourceChange::source_file_edits(edit))) |
257 | } | 257 | } |
258 | 258 | ||
259 | #[cfg(test)] | 259 | #[cfg(test)] |
@@ -642,7 +642,6 @@ mod tests { | |||
642 | RangeInfo { | 642 | RangeInfo { |
643 | range: 4..7, | 643 | range: 4..7, |
644 | info: SourceChange { | 644 | info: SourceChange { |
645 | label: "Rename", | ||
646 | source_file_edits: [ | 645 | source_file_edits: [ |
647 | SourceFileEdit { | 646 | SourceFileEdit { |
648 | file_id: FileId( | 647 | file_id: FileId( |
@@ -669,7 +668,6 @@ mod tests { | |||
669 | dst_path: "bar/foo2.rs", | 668 | dst_path: "bar/foo2.rs", |
670 | }, | 669 | }, |
671 | ], | 670 | ], |
672 | cursor_position: None, | ||
673 | is_snippet: false, | 671 | is_snippet: false, |
674 | }, | 672 | }, |
675 | }, | 673 | }, |
@@ -695,7 +693,6 @@ mod tests { | |||
695 | RangeInfo { | 693 | RangeInfo { |
696 | range: 4..7, | 694 | range: 4..7, |
697 | info: SourceChange { | 695 | info: SourceChange { |
698 | label: "Rename", | ||
699 | source_file_edits: [ | 696 | source_file_edits: [ |
700 | SourceFileEdit { | 697 | SourceFileEdit { |
701 | file_id: FileId( | 698 | file_id: FileId( |
@@ -722,7 +719,6 @@ mod tests { | |||
722 | dst_path: "foo2/mod.rs", | 719 | dst_path: "foo2/mod.rs", |
723 | }, | 720 | }, |
724 | ], | 721 | ], |
725 | cursor_position: None, | ||
726 | is_snippet: false, | 722 | is_snippet: false, |
727 | }, | 723 | }, |
728 | }, | 724 | }, |
@@ -779,7 +775,6 @@ mod tests { | |||
779 | RangeInfo { | 775 | RangeInfo { |
780 | range: 8..11, | 776 | range: 8..11, |
781 | info: SourceChange { | 777 | info: SourceChange { |
782 | label: "Rename", | ||
783 | source_file_edits: [ | 778 | source_file_edits: [ |
784 | SourceFileEdit { | 779 | SourceFileEdit { |
785 | file_id: FileId( | 780 | file_id: FileId( |
@@ -819,7 +814,6 @@ mod tests { | |||
819 | dst_path: "bar/foo2.rs", | 814 | dst_path: "bar/foo2.rs", |
820 | }, | 815 | }, |
821 | ], | 816 | ], |
822 | cursor_position: None, | ||
823 | is_snippet: false, | 817 | is_snippet: false, |
824 | }, | 818 | }, |
825 | }, | 819 | }, |
@@ -986,8 +980,8 @@ mod tests { | |||
986 | if let Some(change) = source_change { | 980 | if let Some(change) = source_change { |
987 | for edit in change.info.source_file_edits { | 981 | for edit in change.info.source_file_edits { |
988 | file_id = Some(edit.file_id); | 982 | file_id = Some(edit.file_id); |
989 | for indel in edit.edit.as_indels() { | 983 | for indel in edit.edit.into_iter() { |
990 | text_edit_builder.replace(indel.delete, indel.insert.clone()); | 984 | text_edit_builder.replace(indel.delete, indel.insert); |
991 | } | 985 | } |
992 | } | 986 | } |
993 | } | 987 | } |
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs index 131b8f307..6e7e47199 100644 --- a/crates/ra_ide/src/runnables.rs +++ b/crates/ra_ide/src/runnables.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{AsAssocItem, Semantics}; | 3 | use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
@@ -10,12 +10,14 @@ use ra_syntax::{ | |||
10 | 10 | ||
11 | use crate::FileId; | 11 | use crate::FileId; |
12 | use ast::DocCommentsOwner; | 12 | use ast::DocCommentsOwner; |
13 | use ra_cfg::CfgExpr; | ||
13 | use std::fmt::Display; | 14 | use std::fmt::Display; |
14 | 15 | ||
15 | #[derive(Debug)] | 16 | #[derive(Debug)] |
16 | pub struct Runnable { | 17 | pub struct Runnable { |
17 | pub range: TextRange, | 18 | pub range: TextRange, |
18 | pub kind: RunnableKind, | 19 | pub kind: RunnableKind, |
20 | pub cfg_exprs: Vec<CfgExpr>, | ||
19 | } | 21 | } |
20 | 22 | ||
21 | #[derive(Debug)] | 23 | #[derive(Debug)] |
@@ -45,29 +47,33 @@ pub enum RunnableKind { | |||
45 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 47 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
46 | let sema = Semantics::new(db); | 48 | let sema = Semantics::new(db); |
47 | let source_file = sema.parse(file_id); | 49 | let source_file = sema.parse(file_id); |
48 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect() | 50 | source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() |
49 | } | 51 | } |
50 | 52 | ||
51 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode) -> Option<Runnable> { | 53 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> { |
52 | match_ast! { | 54 | match_ast! { |
53 | match item { | 55 | match item { |
54 | ast::FnDef(it) => runnable_fn(sema, it), | 56 | ast::FnDef(it) => runnable_fn(sema, it, file_id), |
55 | ast::Module(it) => runnable_mod(sema, it), | 57 | ast::Module(it) => runnable_mod(sema, it, file_id), |
56 | _ => None, | 58 | _ => None, |
57 | } | 59 | } |
58 | } | 60 | } |
59 | } | 61 | } |
60 | 62 | ||
61 | fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Runnable> { | 63 | fn runnable_fn( |
64 | sema: &Semantics<RootDatabase>, | ||
65 | fn_def: ast::FnDef, | ||
66 | file_id: FileId, | ||
67 | ) -> Option<Runnable> { | ||
62 | let name_string = fn_def.name()?.text().to_string(); | 68 | let name_string = fn_def.name()?.text().to_string(); |
63 | 69 | ||
64 | let kind = if name_string == "main" { | 70 | let kind = if name_string == "main" { |
65 | RunnableKind::Bin | 71 | RunnableKind::Bin |
66 | } else { | 72 | } else { |
67 | let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) { | 73 | let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) { |
68 | let def = sema.to_def(&fn_def)?; | 74 | Some(module) => { |
69 | let impl_trait_name = | 75 | let def = sema.to_def(&fn_def)?; |
70 | def.as_assoc_item(sema.db).and_then(|assoc_item| { | 76 | let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| { |
71 | match assoc_item.container(sema.db) { | 77 | match assoc_item.container(sema.db) { |
72 | hir::AssocItemContainer::Trait(trait_item) => { | 78 | hir::AssocItemContainer::Trait(trait_item) => { |
73 | Some(trait_item.name(sema.db).to_string()) | 79 | Some(trait_item.name(sema.db).to_string()) |
@@ -79,25 +85,25 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run | |||
79 | } | 85 | } |
80 | }); | 86 | }); |
81 | 87 | ||
82 | let path_iter = module | 88 | let path_iter = module |
83 | .path_to_root(sema.db) | 89 | .path_to_root(sema.db) |
84 | .into_iter() | 90 | .into_iter() |
85 | .rev() | 91 | .rev() |
86 | .filter_map(|it| it.name(sema.db)) | 92 | .filter_map(|it| it.name(sema.db)) |
87 | .map(|name| name.to_string()); | 93 | .map(|name| name.to_string()); |
88 | 94 | ||
89 | let path = if let Some(impl_trait_name) = impl_trait_name { | 95 | let path = if let Some(impl_trait_name) = impl_trait_name { |
90 | path_iter | 96 | path_iter |
91 | .chain(std::iter::once(impl_trait_name)) | 97 | .chain(std::iter::once(impl_trait_name)) |
92 | .chain(std::iter::once(name_string)) | 98 | .chain(std::iter::once(name_string)) |
93 | .join("::") | 99 | .join("::") |
94 | } else { | 100 | } else { |
95 | path_iter.chain(std::iter::once(name_string)).join("::") | 101 | path_iter.chain(std::iter::once(name_string)).join("::") |
96 | }; | 102 | }; |
97 | 103 | ||
98 | TestId::Path(path) | 104 | TestId::Path(path) |
99 | } else { | 105 | } |
100 | TestId::Name(name_string) | 106 | None => TestId::Name(name_string), |
101 | }; | 107 | }; |
102 | 108 | ||
103 | if has_test_related_attribute(&fn_def) { | 109 | if has_test_related_attribute(&fn_def) { |
@@ -111,7 +117,12 @@ fn runnable_fn(sema: &Semantics<RootDatabase>, fn_def: ast::FnDef) -> Option<Run | |||
111 | return None; | 117 | return None; |
112 | } | 118 | } |
113 | }; | 119 | }; |
114 | Some(Runnable { range: fn_def.syntax().text_range(), kind }) | 120 | |
121 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def)); | ||
122 | let cfg_exprs = | ||
123 | attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); | ||
124 | |||
125 | Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) | ||
115 | } | 126 | } |
116 | 127 | ||
117 | #[derive(Debug)] | 128 | #[derive(Debug)] |
@@ -147,7 +158,11 @@ fn has_doc_test(fn_def: &ast::FnDef) -> bool { | |||
147 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) | 158 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) |
148 | } | 159 | } |
149 | 160 | ||
150 | fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<Runnable> { | 161 | fn runnable_mod( |
162 | sema: &Semantics<RootDatabase>, | ||
163 | module: ast::Module, | ||
164 | file_id: FileId, | ||
165 | ) -> Option<Runnable> { | ||
151 | let has_test_function = module | 166 | let has_test_function = module |
152 | .item_list()? | 167 | .item_list()? |
153 | .items() | 168 | .items() |
@@ -160,11 +175,20 @@ fn runnable_mod(sema: &Semantics<RootDatabase>, module: ast::Module) -> Option<R | |||
160 | return None; | 175 | return None; |
161 | } | 176 | } |
162 | let range = module.syntax().text_range(); | 177 | let range = module.syntax().text_range(); |
163 | let module = sema.to_def(&module)?; | 178 | let module_def = sema.to_def(&module)?; |
164 | 179 | ||
165 | let path = | 180 | let path = module_def |
166 | module.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::"); | 181 | .path_to_root(sema.db) |
167 | Some(Runnable { range, kind: RunnableKind::TestMod { path } }) | 182 | .into_iter() |
183 | .rev() | ||
184 | .filter_map(|it| it.name(sema.db)) | ||
185 | .join("::"); | ||
186 | |||
187 | let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module)); | ||
188 | let cfg_exprs = | ||
189 | attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); | ||
190 | |||
191 | Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) | ||
168 | } | 192 | } |
169 | 193 | ||
170 | #[cfg(test)] | 194 | #[cfg(test)] |
@@ -196,6 +220,7 @@ mod tests { | |||
196 | Runnable { | 220 | Runnable { |
197 | range: 1..21, | 221 | range: 1..21, |
198 | kind: Bin, | 222 | kind: Bin, |
223 | cfg_exprs: [], | ||
199 | }, | 224 | }, |
200 | Runnable { | 225 | Runnable { |
201 | range: 22..46, | 226 | range: 22..46, |
@@ -207,6 +232,7 @@ mod tests { | |||
207 | ignore: false, | 232 | ignore: false, |
208 | }, | 233 | }, |
209 | }, | 234 | }, |
235 | cfg_exprs: [], | ||
210 | }, | 236 | }, |
211 | Runnable { | 237 | Runnable { |
212 | range: 47..81, | 238 | range: 47..81, |
@@ -218,6 +244,7 @@ mod tests { | |||
218 | ignore: true, | 244 | ignore: true, |
219 | }, | 245 | }, |
220 | }, | 246 | }, |
247 | cfg_exprs: [], | ||
221 | }, | 248 | }, |
222 | ] | 249 | ] |
223 | "### | 250 | "### |
@@ -245,6 +272,7 @@ mod tests { | |||
245 | Runnable { | 272 | Runnable { |
246 | range: 1..21, | 273 | range: 1..21, |
247 | kind: Bin, | 274 | kind: Bin, |
275 | cfg_exprs: [], | ||
248 | }, | 276 | }, |
249 | Runnable { | 277 | Runnable { |
250 | range: 22..64, | 278 | range: 22..64, |
@@ -253,6 +281,7 @@ mod tests { | |||
253 | "foo", | 281 | "foo", |
254 | ), | 282 | ), |
255 | }, | 283 | }, |
284 | cfg_exprs: [], | ||
256 | }, | 285 | }, |
257 | ] | 286 | ] |
258 | "### | 287 | "### |
@@ -283,6 +312,7 @@ mod tests { | |||
283 | Runnable { | 312 | Runnable { |
284 | range: 1..21, | 313 | range: 1..21, |
285 | kind: Bin, | 314 | kind: Bin, |
315 | cfg_exprs: [], | ||
286 | }, | 316 | }, |
287 | Runnable { | 317 | Runnable { |
288 | range: 51..105, | 318 | range: 51..105, |
@@ -291,6 +321,7 @@ mod tests { | |||
291 | "Data::foo", | 321 | "Data::foo", |
292 | ), | 322 | ), |
293 | }, | 323 | }, |
324 | cfg_exprs: [], | ||
294 | }, | 325 | }, |
295 | ] | 326 | ] |
296 | "### | 327 | "### |
@@ -318,6 +349,7 @@ mod tests { | |||
318 | kind: TestMod { | 349 | kind: TestMod { |
319 | path: "test_mod", | 350 | path: "test_mod", |
320 | }, | 351 | }, |
352 | cfg_exprs: [], | ||
321 | }, | 353 | }, |
322 | Runnable { | 354 | Runnable { |
323 | range: 28..57, | 355 | range: 28..57, |
@@ -329,6 +361,7 @@ mod tests { | |||
329 | ignore: false, | 361 | ignore: false, |
330 | }, | 362 | }, |
331 | }, | 363 | }, |
364 | cfg_exprs: [], | ||
332 | }, | 365 | }, |
333 | ] | 366 | ] |
334 | "### | 367 | "### |
@@ -358,6 +391,7 @@ mod tests { | |||
358 | kind: TestMod { | 391 | kind: TestMod { |
359 | path: "foo::test_mod", | 392 | path: "foo::test_mod", |
360 | }, | 393 | }, |
394 | cfg_exprs: [], | ||
361 | }, | 395 | }, |
362 | Runnable { | 396 | Runnable { |
363 | range: 46..79, | 397 | range: 46..79, |
@@ -369,6 +403,7 @@ mod tests { | |||
369 | ignore: false, | 403 | ignore: false, |
370 | }, | 404 | }, |
371 | }, | 405 | }, |
406 | cfg_exprs: [], | ||
372 | }, | 407 | }, |
373 | ] | 408 | ] |
374 | "### | 409 | "### |
@@ -400,6 +435,7 @@ mod tests { | |||
400 | kind: TestMod { | 435 | kind: TestMod { |
401 | path: "foo::bar::test_mod", | 436 | path: "foo::bar::test_mod", |
402 | }, | 437 | }, |
438 | cfg_exprs: [], | ||
403 | }, | 439 | }, |
404 | Runnable { | 440 | Runnable { |
405 | range: 68..105, | 441 | range: 68..105, |
@@ -411,6 +447,89 @@ mod tests { | |||
411 | ignore: false, | 447 | ignore: false, |
412 | }, | 448 | }, |
413 | }, | 449 | }, |
450 | cfg_exprs: [], | ||
451 | }, | ||
452 | ] | ||
453 | "### | ||
454 | ); | ||
455 | } | ||
456 | |||
457 | #[test] | ||
458 | fn test_runnables_with_feature() { | ||
459 | let (analysis, pos) = analysis_and_position( | ||
460 | r#" | ||
461 | //- /lib.rs crate:foo cfg:feature=foo | ||
462 | <|> //empty | ||
463 | #[test] | ||
464 | #[cfg(feature = "foo")] | ||
465 | fn test_foo1() {} | ||
466 | "#, | ||
467 | ); | ||
468 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
469 | assert_debug_snapshot!(&runnables, | ||
470 | @r###" | ||
471 | [ | ||
472 | Runnable { | ||
473 | range: 1..58, | ||
474 | kind: Test { | ||
475 | test_id: Path( | ||
476 | "test_foo1", | ||
477 | ), | ||
478 | attr: TestAttr { | ||
479 | ignore: false, | ||
480 | }, | ||
481 | }, | ||
482 | cfg_exprs: [ | ||
483 | KeyValue { | ||
484 | key: "feature", | ||
485 | value: "foo", | ||
486 | }, | ||
487 | ], | ||
488 | }, | ||
489 | ] | ||
490 | "### | ||
491 | ); | ||
492 | } | ||
493 | |||
494 | #[test] | ||
495 | fn test_runnables_with_features() { | ||
496 | let (analysis, pos) = analysis_and_position( | ||
497 | r#" | ||
498 | //- /lib.rs crate:foo cfg:feature=foo,feature=bar | ||
499 | <|> //empty | ||
500 | #[test] | ||
501 | #[cfg(all(feature = "foo", feature = "bar"))] | ||
502 | fn test_foo1() {} | ||
503 | "#, | ||
504 | ); | ||
505 | let runnables = analysis.runnables(pos.file_id).unwrap(); | ||
506 | assert_debug_snapshot!(&runnables, | ||
507 | @r###" | ||
508 | [ | ||
509 | Runnable { | ||
510 | range: 1..80, | ||
511 | kind: Test { | ||
512 | test_id: Path( | ||
513 | "test_foo1", | ||
514 | ), | ||
515 | attr: TestAttr { | ||
516 | ignore: false, | ||
517 | }, | ||
518 | }, | ||
519 | cfg_exprs: [ | ||
520 | All( | ||
521 | [ | ||
522 | KeyValue { | ||
523 | key: "feature", | ||
524 | value: "foo", | ||
525 | }, | ||
526 | KeyValue { | ||
527 | key: "feature", | ||
528 | value: "bar", | ||
529 | }, | ||
530 | ], | ||
531 | ), | ||
532 | ], | ||
414 | }, | 533 | }, |
415 | ] | 534 | ] |
416 | "### | 535 | "### |
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html index ea026d7a0..68fc589bc 100644 --- a/crates/ra_ide/src/snapshots/highlight_injection.html +++ b/crates/ra_ide/src/snapshots/highlight_injection.html | |||
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
17 | .type_param { color: #DFAF8F; } | 17 | .type_param { color: #DFAF8F; } |
18 | .attribute { color: #94BFF3; } | 18 | .attribute { color: #94BFF3; } |
19 | .numeric_literal { color: #BFEBBF; } | 19 | .numeric_literal { color: #BFEBBF; } |
20 | .bool_literal { color: #BFE6EB; } | ||
20 | .macro { color: #94BFF3; } | 21 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 22 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 23 | .variable { color: #DCDCCC; } |
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index 752b487e8..326744361 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html | |||
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
17 | .type_param { color: #DFAF8F; } | 17 | .type_param { color: #DFAF8F; } |
18 | .attribute { color: #94BFF3; } | 18 | .attribute { color: #94BFF3; } |
19 | .numeric_literal { color: #BFEBBF; } | 19 | .numeric_literal { color: #BFEBBF; } |
20 | .bool_literal { color: #BFE6EB; } | ||
20 | .macro { color: #94BFF3; } | 21 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 22 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 23 | .variable { color: #DCDCCC; } |
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 635fe5cf9..352e35095 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html | |||
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
17 | .type_param { color: #DFAF8F; } | 17 | .type_param { color: #DFAF8F; } |
18 | .attribute { color: #94BFF3; } | 18 | .attribute { color: #94BFF3; } |
19 | .numeric_literal { color: #BFEBBF; } | 19 | .numeric_literal { color: #BFEBBF; } |
20 | .bool_literal { color: #BFE6EB; } | ||
20 | .macro { color: #94BFF3; } | 21 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 22 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 23 | .variable { color: #DCDCCC; } |
@@ -27,19 +28,19 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
27 | .keyword.unsafe { color: #BC8383; font-weight: bold; } | 28 | .keyword.unsafe { color: #BC8383; font-weight: bold; } |
28 | .control { font-style: italic; } | 29 | .control { font-style: italic; } |
29 | </style> | 30 | </style> |
30 | <pre><code><span class="attribute">#[derive(Clone, Debug)]</span> | 31 | <pre><code><span class="attribute">#[</span><span class="function attribute">derive</span><span class="attribute">(Clone, Debug)]</span> |
31 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> { | 32 | <span class="keyword">struct</span> <span class="struct declaration">Foo</span> { |
32 | <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>, | 33 | <span class="keyword">pub</span> <span class="field declaration">x</span>: <span class="builtin_type">i32</span>, |
33 | <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, | 34 | <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, |
34 | } | 35 | } |
35 | 36 | ||
36 | <span class="keyword">trait</span> <span class="trait declaration">Bar</span> { | 37 | <span class="keyword">trait</span> <span class="trait declaration">Bar</span> { |
37 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -> <span class="builtin_type">i32</span>; | 38 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -> <span class="builtin_type">i32</span>; |
38 | } | 39 | } |
39 | 40 | ||
40 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> { | 41 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> { |
41 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -> <span class="builtin_type">i32</span> { | 42 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="self_keyword">self</span>) -> <span class="builtin_type">i32</span> { |
42 | <span class="keyword">self</span>.<span class="field">x</span> | 43 | <span class="self_keyword">self</span>.<span class="field">x</span> |
43 | } | 44 | } |
44 | } | 45 | } |
45 | 46 | ||
@@ -64,7 +65,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
64 | <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>); | 65 | <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>); |
65 | 66 | ||
66 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>(); | 67 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">vec</span> = <span class="unresolved_reference">Vec</span>::<span class="unresolved_reference">new</span>(); |
67 | <span class="keyword control">if</span> <span class="keyword">true</span> { | 68 | <span class="keyword control">if</span> <span class="bool_literal">true</span> { |
68 | <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>; | 69 | <span class="keyword">let</span> <span class="variable declaration">x</span> = <span class="numeric_literal">92</span>; |
69 | <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> }); | 70 | <span class="variable mutable">vec</span>.<span class="unresolved_reference">push</span>(<span class="struct">Foo</span> { <span class="field">x</span>, <span class="field">y</span>: <span class="numeric_literal">1</span> }); |
70 | } | 71 | } |
@@ -91,7 +92,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
91 | <span class="keyword">use</span> <span class="enum">Option</span>::*; | 92 | <span class="keyword">use</span> <span class="enum">Option</span>::*; |
92 | 93 | ||
93 | <span class="keyword">impl</span><<span class="type_param declaration">T</span>> <span class="enum">Option</span><<span class="type_param">T</span>> { | 94 | <span class="keyword">impl</span><<span class="type_param declaration">T</span>> <span class="enum">Option</span><<span class="type_param">T</span>> { |
94 | <span class="keyword">fn</span> <span class="function declaration">and</span><<span class="type_param declaration">U</span>>(<span class="keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span><<span class="type_param">U</span>>) -> <span class="enum">Option</span><(<span class="type_param">T</span>, <span class="type_param">U</span>)> { | 95 | <span class="keyword">fn</span> <span class="function declaration">and</span><<span class="type_param declaration">U</span>>(<span class="self_keyword">self</span>, <span class="variable declaration">other</span>: <span class="enum">Option</span><<span class="type_param">U</span>>) -> <span class="enum">Option</span><(<span class="type_param">T</span>, <span class="type_param">U</span>)> { |
95 | <span class="keyword control">match</span> <span class="variable">other</span> { | 96 | <span class="keyword control">match</span> <span class="variable">other</span> { |
96 | <span class="enum_variant">None</span> => <span class="macro">unimplemented!</span>(), | 97 | <span class="enum_variant">None</span> => <span class="macro">unimplemented!</span>(), |
97 | <span class="variable declaration">Nope</span> => <span class="variable">Nope</span>, | 98 | <span class="variable declaration">Nope</span> => <span class="variable">Nope</span>, |
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index 11e1f3e44..2a0294f71 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html | |||
@@ -17,6 +17,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
17 | .type_param { color: #DFAF8F; } | 17 | .type_param { color: #DFAF8F; } |
18 | .attribute { color: #94BFF3; } | 18 | .attribute { color: #94BFF3; } |
19 | .numeric_literal { color: #BFEBBF; } | 19 | .numeric_literal { color: #BFEBBF; } |
20 | .bool_literal { color: #BFE6EB; } | ||
20 | .macro { color: #94BFF3; } | 21 | .macro { color: #94BFF3; } |
21 | .module { color: #AFD8AF; } | 22 | .module { color: #AFD8AF; } |
22 | .variable { color: #DCDCCC; } | 23 | .variable { color: #DCDCCC; } |
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs index 1873d1d0d..130d3b4c3 100644 --- a/crates/ra_ide/src/ssr.rs +++ b/crates/ra_ide/src/ssr.rs | |||
@@ -196,10 +196,10 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches { | |||
196 | ) -> Option<Match> { | 196 | ) -> Option<Match> { |
197 | let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?; | 197 | let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?; |
198 | 198 | ||
199 | let mut pattern_fields = | 199 | let mut pattern_fields: Vec<RecordField> = |
200 | pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); | 200 | pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or_default(); |
201 | let mut code_fields = | 201 | let mut code_fields: Vec<RecordField> = |
202 | code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); | 202 | code.record_field_list().map(|x| x.fields().collect()).unwrap_or_default(); |
203 | 203 | ||
204 | if pattern_fields.len() != code_fields.len() { | 204 | if pattern_fields.len() != code_fields.len() { |
205 | return None; | 205 | return None; |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index be57eeb0a..8a995d779 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -361,7 +361,9 @@ fn highlight_element( | |||
361 | } | 361 | } |
362 | 362 | ||
363 | // Highlight references like the definitions they resolve to | 363 | // Highlight references like the definitions they resolve to |
364 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None, | 364 | NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { |
365 | Highlight::from(HighlightTag::Function) | HighlightModifier::Attribute | ||
366 | } | ||
365 | NAME_REF => { | 367 | NAME_REF => { |
366 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); | 368 | let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); |
367 | match classify_name_ref(sema, &name_ref) { | 369 | match classify_name_ref(sema, &name_ref) { |
@@ -411,6 +413,8 @@ fn highlight_element( | |||
411 | | T![in] => h | HighlightModifier::ControlFlow, | 413 | | T![in] => h | HighlightModifier::ControlFlow, |
412 | T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, | 414 | T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, |
413 | T![unsafe] => h | HighlightModifier::Unsafe, | 415 | T![unsafe] => h | HighlightModifier::Unsafe, |
416 | T![true] | T![false] => HighlightTag::BoolLiteral.into(), | ||
417 | T![self] => HighlightTag::SelfKeyword.into(), | ||
414 | _ => h, | 418 | _ => h, |
415 | } | 419 | } |
416 | } | 420 | } |
@@ -478,23 +482,31 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { | |||
478 | } | 482 | } |
479 | 483 | ||
480 | fn highlight_name_by_syntax(name: ast::Name) -> Highlight { | 484 | fn highlight_name_by_syntax(name: ast::Name) -> Highlight { |
481 | let default = HighlightTag::Function.into(); | 485 | let default = HighlightTag::UnresolvedReference; |
482 | 486 | ||
483 | let parent = match name.syntax().parent() { | 487 | let parent = match name.syntax().parent() { |
484 | Some(it) => it, | 488 | Some(it) => it, |
485 | _ => return default, | 489 | _ => return default.into(), |
486 | }; | 490 | }; |
487 | 491 | ||
488 | match parent.kind() { | 492 | let tag = match parent.kind() { |
489 | STRUCT_DEF => HighlightTag::Struct.into(), | 493 | STRUCT_DEF => HighlightTag::Struct, |
490 | ENUM_DEF => HighlightTag::Enum.into(), | 494 | ENUM_DEF => HighlightTag::Enum, |
491 | UNION_DEF => HighlightTag::Union.into(), | 495 | UNION_DEF => HighlightTag::Union, |
492 | TRAIT_DEF => HighlightTag::Trait.into(), | 496 | TRAIT_DEF => HighlightTag::Trait, |
493 | TYPE_ALIAS_DEF => HighlightTag::TypeAlias.into(), | 497 | TYPE_ALIAS_DEF => HighlightTag::TypeAlias, |
494 | TYPE_PARAM => HighlightTag::TypeParam.into(), | 498 | TYPE_PARAM => HighlightTag::TypeParam, |
495 | RECORD_FIELD_DEF => HighlightTag::Field.into(), | 499 | RECORD_FIELD_DEF => HighlightTag::Field, |
500 | MODULE => HighlightTag::Module, | ||
501 | FN_DEF => HighlightTag::Function, | ||
502 | CONST_DEF => HighlightTag::Constant, | ||
503 | STATIC_DEF => HighlightTag::Static, | ||
504 | ENUM_VARIANT => HighlightTag::EnumVariant, | ||
505 | BIND_PAT => HighlightTag::Local, | ||
496 | _ => default, | 506 | _ => default, |
497 | } | 507 | }; |
508 | |||
509 | tag.into() | ||
498 | } | 510 | } |
499 | 511 | ||
500 | fn highlight_injection( | 512 | fn highlight_injection( |
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs index ff0eeeb52..edfe61f39 100644 --- a/crates/ra_ide/src/syntax_highlighting/html.rs +++ b/crates/ra_ide/src/syntax_highlighting/html.rs | |||
@@ -76,6 +76,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
76 | .type_param { color: #DFAF8F; } | 76 | .type_param { color: #DFAF8F; } |
77 | .attribute { color: #94BFF3; } | 77 | .attribute { color: #94BFF3; } |
78 | .numeric_literal { color: #BFEBBF; } | 78 | .numeric_literal { color: #BFEBBF; } |
79 | .bool_literal { color: #BFE6EB; } | ||
79 | .macro { color: #94BFF3; } | 80 | .macro { color: #94BFF3; } |
80 | .module { color: #AFD8AF; } | 81 | .module { color: #AFD8AF; } |
81 | .variable { color: #DCDCCC; } | 82 | .variable { color: #DCDCCC; } |
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs index be1a0f12b..46c718c91 100644 --- a/crates/ra_ide/src/syntax_highlighting/tags.rs +++ b/crates/ra_ide/src/syntax_highlighting/tags.rs | |||
@@ -15,6 +15,7 @@ pub struct HighlightModifiers(u32); | |||
15 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 15 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
16 | pub enum HighlightTag { | 16 | pub enum HighlightTag { |
17 | Attribute, | 17 | Attribute, |
18 | BoolLiteral, | ||
18 | BuiltinType, | 19 | BuiltinType, |
19 | ByteLiteral, | 20 | ByteLiteral, |
20 | CharLiteral, | 21 | CharLiteral, |
@@ -29,6 +30,7 @@ pub enum HighlightTag { | |||
29 | Macro, | 30 | Macro, |
30 | Module, | 31 | Module, |
31 | NumericLiteral, | 32 | NumericLiteral, |
33 | SelfKeyword, | ||
32 | SelfType, | 34 | SelfType, |
33 | Static, | 35 | Static, |
34 | StringLiteral, | 36 | StringLiteral, |
@@ -45,8 +47,10 @@ pub enum HighlightTag { | |||
45 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 47 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
46 | #[repr(u8)] | 48 | #[repr(u8)] |
47 | pub enum HighlightModifier { | 49 | pub enum HighlightModifier { |
50 | /// Used to differentiate individual elements within attributes. | ||
51 | Attribute = 0, | ||
48 | /// Used with keywords like `if` and `break`. | 52 | /// Used with keywords like `if` and `break`. |
49 | ControlFlow = 0, | 53 | ControlFlow, |
50 | /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is | 54 | /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is |
51 | /// not. | 55 | /// not. |
52 | Definition, | 56 | Definition, |
@@ -58,6 +62,7 @@ impl HighlightTag { | |||
58 | fn as_str(self) -> &'static str { | 62 | fn as_str(self) -> &'static str { |
59 | match self { | 63 | match self { |
60 | HighlightTag::Attribute => "attribute", | 64 | HighlightTag::Attribute => "attribute", |
65 | HighlightTag::BoolLiteral => "bool_literal", | ||
61 | HighlightTag::BuiltinType => "builtin_type", | 66 | HighlightTag::BuiltinType => "builtin_type", |
62 | HighlightTag::ByteLiteral => "byte_literal", | 67 | HighlightTag::ByteLiteral => "byte_literal", |
63 | HighlightTag::CharLiteral => "char_literal", | 68 | HighlightTag::CharLiteral => "char_literal", |
@@ -72,6 +77,7 @@ impl HighlightTag { | |||
72 | HighlightTag::Macro => "macro", | 77 | HighlightTag::Macro => "macro", |
73 | HighlightTag::Module => "module", | 78 | HighlightTag::Module => "module", |
74 | HighlightTag::NumericLiteral => "numeric_literal", | 79 | HighlightTag::NumericLiteral => "numeric_literal", |
80 | HighlightTag::SelfKeyword => "self_keyword", | ||
75 | HighlightTag::SelfType => "self_type", | 81 | HighlightTag::SelfType => "self_type", |
76 | HighlightTag::Static => "static", | 82 | HighlightTag::Static => "static", |
77 | HighlightTag::StringLiteral => "string_literal", | 83 | HighlightTag::StringLiteral => "string_literal", |
@@ -95,6 +101,7 @@ impl fmt::Display for HighlightTag { | |||
95 | 101 | ||
96 | impl HighlightModifier { | 102 | impl HighlightModifier { |
97 | const ALL: &'static [HighlightModifier] = &[ | 103 | const ALL: &'static [HighlightModifier] = &[ |
104 | HighlightModifier::Attribute, | ||
98 | HighlightModifier::ControlFlow, | 105 | HighlightModifier::ControlFlow, |
99 | HighlightModifier::Definition, | 106 | HighlightModifier::Definition, |
100 | HighlightModifier::Mutable, | 107 | HighlightModifier::Mutable, |
@@ -103,6 +110,7 @@ impl HighlightModifier { | |||
103 | 110 | ||
104 | fn as_str(self) -> &'static str { | 111 | fn as_str(self) -> &'static str { |
105 | match self { | 112 | match self { |
113 | HighlightModifier::Attribute => "attribute", | ||
106 | HighlightModifier::ControlFlow => "control", | 114 | HighlightModifier::ControlFlow => "control", |
107 | HighlightModifier::Definition => "declaration", | 115 | HighlightModifier::Definition => "declaration", |
108 | HighlightModifier::Mutable => "mutable", | 116 | HighlightModifier::Mutable => "mutable", |
diff --git a/crates/ra_ide/src/test_utils.rs b/crates/ra_ide/src/test_utils.rs deleted file mode 100644 index 48c8fd1f4..000000000 --- a/crates/ra_ide/src/test_utils.rs +++ /dev/null | |||
@@ -1,25 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use ra_syntax::{SourceFile, TextSize}; | ||
4 | use ra_text_edit::TextEdit; | ||
5 | |||
6 | pub use test_utils::*; | ||
7 | |||
8 | pub fn check_action<F: Fn(&SourceFile, TextSize) -> Option<TextEdit>>( | ||
9 | before: &str, | ||
10 | after: &str, | ||
11 | f: F, | ||
12 | ) { | ||
13 | let (before_cursor_pos, before) = extract_offset(before); | ||
14 | let file = SourceFile::parse(&before).ok().unwrap(); | ||
15 | let result = f(&file, before_cursor_pos).expect("code action is not applicable"); | ||
16 | let actual = { | ||
17 | let mut actual = before.to_string(); | ||
18 | result.apply(&mut actual); | ||
19 | actual | ||
20 | }; | ||
21 | let actual_cursor_pos = | ||
22 | result.apply_to_offset(before_cursor_pos).expect("cursor position is affected by the edit"); | ||
23 | let actual = add_cursor(&actual, actual_cursor_pos); | ||
24 | assert_eq_text!(after, &actual); | ||
25 | } | ||
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs index 6f04f0be4..39bb3b357 100644 --- a/crates/ra_ide/src/typing.rs +++ b/crates/ra_ide/src/typing.rs | |||
@@ -17,7 +17,7 @@ mod on_enter; | |||
17 | 17 | ||
18 | use ra_db::{FilePosition, SourceDatabase}; | 18 | use ra_db::{FilePosition, SourceDatabase}; |
19 | use ra_fmt::leading_indent; | 19 | use ra_fmt::leading_indent; |
20 | use ra_ide_db::{source_change::SingleFileChange, RootDatabase}; | 20 | use ra_ide_db::RootDatabase; |
21 | use ra_syntax::{ | 21 | use ra_syntax::{ |
22 | algo::find_node_at_offset, | 22 | algo::find_node_at_offset, |
23 | ast::{self, AstToken}, | 23 | ast::{self, AstToken}, |
@@ -40,15 +40,11 @@ pub(crate) fn on_char_typed( | |||
40 | assert!(TRIGGER_CHARS.contains(char_typed)); | 40 | assert!(TRIGGER_CHARS.contains(char_typed)); |
41 | let file = &db.parse(position.file_id).tree(); | 41 | let file = &db.parse(position.file_id).tree(); |
42 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); | 42 | assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed)); |
43 | let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?; | 43 | let text_edit = on_char_typed_inner(file, position.offset, char_typed)?; |
44 | Some(single_file_change.into_source_change(position.file_id)) | 44 | Some(SourceChange::source_file_edit_from(position.file_id, text_edit)) |
45 | } | 45 | } |
46 | 46 | ||
47 | fn on_char_typed_inner( | 47 | fn on_char_typed_inner(file: &SourceFile, offset: TextSize, char_typed: char) -> Option<TextEdit> { |
48 | file: &SourceFile, | ||
49 | offset: TextSize, | ||
50 | char_typed: char, | ||
51 | ) -> Option<SingleFileChange> { | ||
52 | assert!(TRIGGER_CHARS.contains(char_typed)); | 48 | assert!(TRIGGER_CHARS.contains(char_typed)); |
53 | match char_typed { | 49 | match char_typed { |
54 | '.' => on_dot_typed(file, offset), | 50 | '.' => on_dot_typed(file, offset), |
@@ -61,7 +57,7 @@ fn on_char_typed_inner( | |||
61 | /// Returns an edit which should be applied after `=` was typed. Primarily, | 57 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
62 | /// this works when adding `let =`. | 58 | /// this works when adding `let =`. |
63 | // FIXME: use a snippet completion instead of this hack here. | 59 | // FIXME: use a snippet completion instead of this hack here. |
64 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { | 60 | fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
65 | assert_eq!(file.syntax().text().char_at(offset), Some('=')); | 61 | assert_eq!(file.syntax().text().char_at(offset), Some('=')); |
66 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; | 62 | let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; |
67 | if let_stmt.semicolon_token().is_some() { | 63 | if let_stmt.semicolon_token().is_some() { |
@@ -79,15 +75,11 @@ fn on_eq_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> | |||
79 | return None; | 75 | return None; |
80 | } | 76 | } |
81 | let offset = let_stmt.syntax().text_range().end(); | 77 | let offset = let_stmt.syntax().text_range().end(); |
82 | Some(SingleFileChange { | 78 | Some(TextEdit::insert(offset, ";".to_string())) |
83 | label: "add semicolon".to_string(), | ||
84 | edit: TextEdit::insert(offset, ";".to_string()), | ||
85 | cursor_position: None, | ||
86 | }) | ||
87 | } | 79 | } |
88 | 80 | ||
89 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. | 81 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. |
90 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { | 82 | fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
91 | assert_eq!(file.syntax().text().char_at(offset), Some('.')); | 83 | assert_eq!(file.syntax().text().char_at(offset), Some('.')); |
92 | let whitespace = | 84 | let whitespace = |
93 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; | 85 | file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?; |
@@ -108,15 +100,11 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> | |||
108 | return None; | 100 | return None; |
109 | } | 101 | } |
110 | 102 | ||
111 | Some(SingleFileChange { | 103 | Some(TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent)) |
112 | label: "reindent dot".to_string(), | ||
113 | edit: TextEdit::replace(TextRange::new(offset - current_indent_len, offset), target_indent), | ||
114 | cursor_position: Some(offset + target_indent_len - current_indent_len + TextSize::of('.')), | ||
115 | }) | ||
116 | } | 104 | } |
117 | 105 | ||
118 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` | 106 | /// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }` |
119 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChange> { | 107 | fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
120 | let file_text = file.syntax().text(); | 108 | let file_text = file.syntax().text(); |
121 | assert_eq!(file_text.char_at(offset), Some('>')); | 109 | assert_eq!(file_text.char_at(offset), Some('>')); |
122 | let after_arrow = offset + TextSize::of('>'); | 110 | let after_arrow = offset + TextSize::of('>'); |
@@ -127,11 +115,7 @@ fn on_arrow_typed(file: &SourceFile, offset: TextSize) -> Option<SingleFileChang | |||
127 | return None; | 115 | return None; |
128 | } | 116 | } |
129 | 117 | ||
130 | Some(SingleFileChange { | 118 | Some(TextEdit::insert(after_arrow, " ".to_string())) |
131 | label: "add space after return type".to_string(), | ||
132 | edit: TextEdit::insert(after_arrow, " ".to_string()), | ||
133 | cursor_position: Some(after_arrow), | ||
134 | }) | ||
135 | } | 119 | } |
136 | 120 | ||
137 | #[cfg(test)] | 121 | #[cfg(test)] |
@@ -140,29 +124,23 @@ mod tests { | |||
140 | 124 | ||
141 | use super::*; | 125 | use super::*; |
142 | 126 | ||
143 | fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> { | 127 | fn do_type_char(char_typed: char, before: &str) -> Option<String> { |
144 | let (offset, before) = extract_offset(before); | 128 | let (offset, before) = extract_offset(before); |
145 | let edit = TextEdit::insert(offset, char_typed.to_string()); | 129 | let edit = TextEdit::insert(offset, char_typed.to_string()); |
146 | let mut before = before.to_string(); | 130 | let mut before = before.to_string(); |
147 | edit.apply(&mut before); | 131 | edit.apply(&mut before); |
148 | let parse = SourceFile::parse(&before); | 132 | let parse = SourceFile::parse(&before); |
149 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { | 133 | on_char_typed_inner(&parse.tree(), offset, char_typed).map(|it| { |
150 | it.edit.apply(&mut before); | 134 | it.apply(&mut before); |
151 | (before.to_string(), it) | 135 | before.to_string() |
152 | }) | 136 | }) |
153 | } | 137 | } |
154 | 138 | ||
155 | fn type_char(char_typed: char, before: &str, after: &str) { | 139 | fn type_char(char_typed: char, before: &str, after: &str) { |
156 | let (actual, file_change) = do_type_char(char_typed, before) | 140 | let actual = do_type_char(char_typed, before) |
157 | .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); | 141 | .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); |
158 | 142 | ||
159 | if after.contains("<|>") { | 143 | assert_eq_text!(after, &actual); |
160 | let (offset, after) = extract_offset(after); | ||
161 | assert_eq_text!(&after, &actual); | ||
162 | assert_eq!(file_change.cursor_position, Some(offset)) | ||
163 | } else { | ||
164 | assert_eq_text!(after, &actual); | ||
165 | } | ||
166 | } | 144 | } |
167 | 145 | ||
168 | fn type_char_noop(char_typed: char, before: &str) { | 146 | fn type_char_noop(char_typed: char, before: &str) { |
@@ -350,6 +328,6 @@ fn foo() { | |||
350 | 328 | ||
351 | #[test] | 329 | #[test] |
352 | fn adds_space_after_return_type() { | 330 | fn adds_space_after_return_type() { |
353 | type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }") | 331 | type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -> { 92 }") |
354 | } | 332 | } |
355 | } | 333 | } |
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs index 78a40cc94..a40d8af9c 100644 --- a/crates/ra_ide/src/typing/on_enter.rs +++ b/crates/ra_ide/src/typing/on_enter.rs | |||
@@ -11,9 +11,7 @@ use ra_syntax::{ | |||
11 | }; | 11 | }; |
12 | use ra_text_edit::TextEdit; | 12 | use ra_text_edit::TextEdit; |
13 | 13 | ||
14 | use crate::{SourceChange, SourceFileEdit}; | 14 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { |
15 | |||
16 | pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> { | ||
17 | let parse = db.parse(position.file_id); | 15 | let parse = db.parse(position.file_id); |
18 | let file = parse.tree(); | 16 | let file = parse.tree(); |
19 | let comment = file | 17 | let comment = file |
@@ -38,17 +36,10 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour | |||
38 | } | 36 | } |
39 | 37 | ||
40 | let indent = node_indent(&file, comment.syntax())?; | 38 | let indent = node_indent(&file, comment.syntax())?; |
41 | let inserted = format!("\n{}{} ", indent, prefix); | 39 | let inserted = format!("\n{}{} $0", indent, prefix); |
42 | let cursor_position = position.offset + TextSize::of(&inserted); | ||
43 | let edit = TextEdit::insert(position.offset, inserted); | 40 | let edit = TextEdit::insert(position.offset, inserted); |
44 | 41 | ||
45 | Some( | 42 | Some(edit) |
46 | SourceChange::source_file_edit( | ||
47 | "On enter", | ||
48 | SourceFileEdit { edit, file_id: position.file_id }, | ||
49 | ) | ||
50 | .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }), | ||
51 | ) | ||
52 | } | 43 | } |
53 | 44 | ||
54 | fn followed_by_comment(comment: &ast::Comment) -> bool { | 45 | fn followed_by_comment(comment: &ast::Comment) -> bool { |
@@ -84,7 +75,7 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> { | |||
84 | 75 | ||
85 | #[cfg(test)] | 76 | #[cfg(test)] |
86 | mod tests { | 77 | mod tests { |
87 | use test_utils::{add_cursor, assert_eq_text, extract_offset}; | 78 | use test_utils::{assert_eq_text, extract_offset}; |
88 | 79 | ||
89 | use crate::mock_analysis::single_file; | 80 | use crate::mock_analysis::single_file; |
90 | 81 | ||
@@ -95,10 +86,8 @@ mod tests { | |||
95 | let (analysis, file_id) = single_file(&before); | 86 | let (analysis, file_id) = single_file(&before); |
96 | let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; | 87 | let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; |
97 | 88 | ||
98 | assert_eq!(result.source_file_edits.len(), 1); | ||
99 | let mut actual = before.to_string(); | 89 | let mut actual = before.to_string(); |
100 | result.source_file_edits[0].edit.apply(&mut actual); | 90 | result.apply(&mut actual); |
101 | let actual = add_cursor(&actual, result.cursor_position.unwrap().offset); | ||
102 | Some(actual) | 91 | Some(actual) |
103 | } | 92 | } |
104 | 93 | ||
@@ -121,7 +110,7 @@ fn foo() { | |||
121 | ", | 110 | ", |
122 | r" | 111 | r" |
123 | /// Some docs | 112 | /// Some docs |
124 | /// <|> | 113 | /// $0 |
125 | fn foo() { | 114 | fn foo() { |
126 | } | 115 | } |
127 | ", | 116 | ", |
@@ -137,7 +126,7 @@ impl S { | |||
137 | r" | 126 | r" |
138 | impl S { | 127 | impl S { |
139 | /// Some | 128 | /// Some |
140 | /// <|> docs. | 129 | /// $0 docs. |
141 | fn foo() {} | 130 | fn foo() {} |
142 | } | 131 | } |
143 | ", | 132 | ", |
@@ -151,7 +140,7 @@ fn foo() { | |||
151 | ", | 140 | ", |
152 | r" | 141 | r" |
153 | /// | 142 | /// |
154 | /// <|> Some docs | 143 | /// $0 Some docs |
155 | fn foo() { | 144 | fn foo() { |
156 | } | 145 | } |
157 | ", | 146 | ", |
@@ -175,7 +164,7 @@ fn main() { | |||
175 | r" | 164 | r" |
176 | fn main() { | 165 | fn main() { |
177 | // Fix | 166 | // Fix |
178 | // <|> me | 167 | // $0 me |
179 | let x = 1 + 1; | 168 | let x = 1 + 1; |
180 | } | 169 | } |
181 | ", | 170 | ", |
@@ -195,7 +184,7 @@ fn main() { | |||
195 | r" | 184 | r" |
196 | fn main() { | 185 | fn main() { |
197 | // Fix | 186 | // Fix |
198 | // <|> | 187 | // $0 |
199 | // me | 188 | // me |
200 | let x = 1 + 1; | 189 | let x = 1 + 1; |
201 | } | 190 | } |