diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/join_lines.rs | 69 | ||||
-rw-r--r-- | crates/ide/src/references.rs | 64 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 17 | ||||
-rw-r--r-- | crates/ide/src/typing.rs | 150 | ||||
-rw-r--r-- | crates/ide/src/typing/on_enter.rs | 115 |
5 files changed, 377 insertions, 38 deletions
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index fe2a349e6..61dcbb399 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | use std::convert::TryFrom; | ||
2 | |||
1 | use ide_assists::utils::extract_trivial_expression; | 3 | use ide_assists::utils::extract_trivial_expression; |
2 | use itertools::Itertools; | 4 | use itertools::Itertools; |
3 | use syntax::{ | 5 | use syntax::{ |
@@ -65,14 +67,6 @@ fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextR | |||
65 | 67 | ||
66 | fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) { | 68 | fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) { |
67 | if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { | 69 | if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { |
68 | let mut string_open_quote = false; | ||
69 | if let Some(string) = ast::String::cast(token.clone()) { | ||
70 | if let Some(range) = string.open_quote_text_range() { | ||
71 | cov_mark::hit!(join_string_literal); | ||
72 | string_open_quote = range.end() == offset; | ||
73 | } | ||
74 | } | ||
75 | |||
76 | let n_spaces_after_line_break = { | 70 | let n_spaces_after_line_break = { |
77 | let suff = &token.text()[TextRange::new( | 71 | let suff = &token.text()[TextRange::new( |
78 | offset - token.text_range().start() + TextSize::of('\n'), | 72 | offset - token.text_range().start() + TextSize::of('\n'), |
@@ -81,8 +75,23 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS | |||
81 | suff.bytes().take_while(|&b| b == b' ').count() | 75 | suff.bytes().take_while(|&b| b == b' ').count() |
82 | }; | 76 | }; |
83 | 77 | ||
78 | let mut no_space = false; | ||
79 | if let Some(string) = ast::String::cast(token.clone()) { | ||
80 | if let Some(range) = string.open_quote_text_range() { | ||
81 | cov_mark::hit!(join_string_literal_open_quote); | ||
82 | no_space |= range.end() == offset; | ||
83 | } | ||
84 | if let Some(range) = string.close_quote_text_range() { | ||
85 | cov_mark::hit!(join_string_literal_close_quote); | ||
86 | no_space |= range.start() | ||
87 | == offset | ||
88 | + TextSize::of('\n') | ||
89 | + TextSize::try_from(n_spaces_after_line_break).unwrap(); | ||
90 | } | ||
91 | } | ||
92 | |||
84 | let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into()); | 93 | let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into()); |
85 | let replace_with = if string_open_quote { "" } else { " " }; | 94 | let replace_with = if no_space { "" } else { " " }; |
86 | edit.replace(range, replace_with.to_string()); | 95 | edit.replace(range, replace_with.to_string()); |
87 | return; | 96 | return; |
88 | } | 97 | } |
@@ -797,22 +806,54 @@ fn foo() { | |||
797 | 806 | ||
798 | #[test] | 807 | #[test] |
799 | fn join_string_literal() { | 808 | fn join_string_literal() { |
800 | cov_mark::check!(join_string_literal); | 809 | { |
801 | check_join_lines( | 810 | cov_mark::check!(join_string_literal_open_quote); |
802 | r#" | 811 | check_join_lines( |
812 | r#" | ||
803 | fn main() { | 813 | fn main() { |
804 | $0" | 814 | $0" |
805 | hello | 815 | hello |
806 | "; | 816 | "; |
807 | } | 817 | } |
808 | "#, | 818 | "#, |
809 | r#" | 819 | r#" |
810 | fn main() { | 820 | fn main() { |
811 | $0"hello | 821 | $0"hello |
812 | "; | 822 | "; |
813 | } | 823 | } |
814 | "#, | 824 | "#, |
815 | ); | 825 | ); |
826 | } | ||
827 | |||
828 | { | ||
829 | cov_mark::check!(join_string_literal_close_quote); | ||
830 | check_join_lines( | ||
831 | r#" | ||
832 | fn main() { | ||
833 | $0"hello | ||
834 | "; | ||
835 | } | ||
836 | "#, | ||
837 | r#" | ||
838 | fn main() { | ||
839 | $0"hello"; | ||
840 | } | ||
841 | "#, | ||
842 | ); | ||
843 | check_join_lines( | ||
844 | r#" | ||
845 | fn main() { | ||
846 | $0r"hello | ||
847 | "; | ||
848 | } | ||
849 | "#, | ||
850 | r#" | ||
851 | fn main() { | ||
852 | $0r"hello"; | ||
853 | } | ||
854 | "#, | ||
855 | ); | ||
856 | } | ||
816 | 857 | ||
817 | check_join_lines( | 858 | check_join_lines( |
818 | r#" | 859 | r#" |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 11ca7ec6b..ae492a264 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -65,7 +65,7 @@ pub(crate) fn find_all_refs( | |||
65 | (find_def(&sema, &syntax, position)?, false) | 65 | (find_def(&sema, &syntax, position)?, false) |
66 | }; | 66 | }; |
67 | 67 | ||
68 | let mut usages = def.usages(sema).set_scope(search_scope).all(); | 68 | let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all(); |
69 | if is_literal_search { | 69 | if is_literal_search { |
70 | // filter for constructor-literals | 70 | // filter for constructor-literals |
71 | let refs = usages.references.values_mut(); | 71 | let refs = usages.references.values_mut(); |
@@ -1163,22 +1163,76 @@ fn foo<const FOO$0: usize>() -> usize { | |||
1163 | } | 1163 | } |
1164 | 1164 | ||
1165 | #[test] | 1165 | #[test] |
1166 | fn test_find_self_ty_in_trait_def() { | 1166 | fn test_trait() { |
1167 | check( | 1167 | check( |
1168 | r#" | 1168 | r#" |
1169 | trait Foo { | 1169 | trait Foo$0 where Self: {} |
1170 | fn f() -> Self$0; | 1170 | |
1171 | impl Foo for () {} | ||
1172 | "#, | ||
1173 | expect![[r#" | ||
1174 | Foo Trait FileId(0) 0..24 6..9 | ||
1175 | |||
1176 | FileId(0) 31..34 | ||
1177 | "#]], | ||
1178 | ); | ||
1179 | } | ||
1180 | |||
1181 | #[test] | ||
1182 | fn test_trait_self() { | ||
1183 | check( | ||
1184 | r#" | ||
1185 | trait Foo where Self$0 { | ||
1186 | fn f() -> Self; | ||
1171 | } | 1187 | } |
1188 | |||
1189 | impl Foo for () {} | ||
1172 | "#, | 1190 | "#, |
1173 | expect![[r#" | 1191 | expect![[r#" |
1174 | Self TypeParam FileId(0) 6..9 6..9 | 1192 | Self TypeParam FileId(0) 6..9 6..9 |
1175 | 1193 | ||
1176 | FileId(0) 26..30 | 1194 | FileId(0) 16..20 |
1195 | FileId(0) 37..41 | ||
1177 | "#]], | 1196 | "#]], |
1178 | ); | 1197 | ); |
1179 | } | 1198 | } |
1180 | 1199 | ||
1181 | #[test] | 1200 | #[test] |
1201 | fn test_self_ty() { | ||
1202 | check( | ||
1203 | r#" | ||
1204 | struct $0Foo; | ||
1205 | |||
1206 | impl Foo where Self: { | ||
1207 | fn f() -> Self; | ||
1208 | } | ||
1209 | "#, | ||
1210 | expect![[r#" | ||
1211 | Foo Struct FileId(0) 0..11 7..10 | ||
1212 | |||
1213 | FileId(0) 18..21 | ||
1214 | FileId(0) 28..32 | ||
1215 | FileId(0) 50..54 | ||
1216 | "#]], | ||
1217 | ); | ||
1218 | check( | ||
1219 | r#" | ||
1220 | struct Foo; | ||
1221 | |||
1222 | impl Foo where Self: { | ||
1223 | fn f() -> Self$0; | ||
1224 | } | ||
1225 | "#, | ||
1226 | expect![[r#" | ||
1227 | impl Impl FileId(0) 13..57 18..21 | ||
1228 | |||
1229 | FileId(0) 18..21 | ||
1230 | FileId(0) 28..32 | ||
1231 | FileId(0) 50..54 | ||
1232 | "#]], | ||
1233 | ); | ||
1234 | } | ||
1235 | #[test] | ||
1182 | fn test_self_variant_with_payload() { | 1236 | fn test_self_variant_with_payload() { |
1183 | check( | 1237 | check( |
1184 | r#" | 1238 | r#" |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 175e7a31d..2bf953305 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -1888,4 +1888,21 @@ impl Foo { | |||
1888 | "error: Cannot rename `Self`", | 1888 | "error: Cannot rename `Self`", |
1889 | ); | 1889 | ); |
1890 | } | 1890 | } |
1891 | |||
1892 | #[test] | ||
1893 | fn test_rename_ignores_self_ty() { | ||
1894 | check( | ||
1895 | "Fo0", | ||
1896 | r#" | ||
1897 | struct $0Foo; | ||
1898 | |||
1899 | impl Foo where Self: {} | ||
1900 | "#, | ||
1901 | r#" | ||
1902 | struct Fo0; | ||
1903 | |||
1904 | impl Fo0 where Self: {} | ||
1905 | "#, | ||
1906 | ); | ||
1907 | } | ||
1891 | } | 1908 | } |
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs index 82c732390..4ad49eca0 100644 --- a/crates/ide/src/typing.rs +++ b/crates/ide/src/typing.rs | |||
@@ -88,7 +88,7 @@ fn on_char_typed_inner( | |||
88 | } | 88 | } |
89 | 89 | ||
90 | /// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a | 90 | /// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a |
91 | /// block. | 91 | /// block, or a part of a `use` item. |
92 | fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { | 92 | fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { |
93 | if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { | 93 | if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { |
94 | return None; | 94 | return None; |
@@ -99,30 +99,59 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option< | |||
99 | // Remove the `{` to get a better parse tree, and reparse | 99 | // Remove the `{` to get a better parse tree, and reparse |
100 | let file = file.reparse(&Indel::delete(brace_token.text_range())); | 100 | let file = file.reparse(&Indel::delete(brace_token.text_range())); |
101 | 101 | ||
102 | let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?; | 102 | if let Some(edit) = brace_expr(&file.tree(), offset) { |
103 | if expr.syntax().text_range().start() != offset { | 103 | return Some(edit); |
104 | return None; | ||
105 | } | 104 | } |
106 | 105 | ||
107 | // Enclose the outermost expression starting at `offset` | 106 | if let Some(edit) = brace_use_path(&file.tree(), offset) { |
108 | while let Some(parent) = expr.syntax().parent() { | 107 | return Some(edit); |
109 | if parent.text_range().start() != expr.syntax().text_range().start() { | 108 | } |
110 | break; | ||
111 | } | ||
112 | 109 | ||
113 | match ast::Expr::cast(parent) { | 110 | return None; |
114 | Some(parent) => expr = parent, | 111 | |
115 | None => break, | 112 | fn brace_use_path(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
113 | let segment: ast::PathSegment = find_node_at_offset(file.syntax(), offset)?; | ||
114 | if segment.syntax().text_range().start() != offset { | ||
115 | return None; | ||
116 | } | 116 | } |
117 | } | ||
118 | 117 | ||
119 | // If it's a statement in a block, we don't know how many statements should be included | 118 | let tree: ast::UseTree = find_node_at_offset(file.syntax(), offset)?; |
120 | if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { | 119 | |
121 | return None; | 120 | Some(TextEdit::insert( |
121 | tree.syntax().text_range().end() + TextSize::of("{"), | ||
122 | "}".to_string(), | ||
123 | )) | ||
122 | } | 124 | } |
123 | 125 | ||
124 | // Insert `}` right after the expression. | 126 | fn brace_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> { |
125 | Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string())) | 127 | let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?; |
128 | if expr.syntax().text_range().start() != offset { | ||
129 | return None; | ||
130 | } | ||
131 | |||
132 | // Enclose the outermost expression starting at `offset` | ||
133 | while let Some(parent) = expr.syntax().parent() { | ||
134 | if parent.text_range().start() != expr.syntax().text_range().start() { | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | match ast::Expr::cast(parent) { | ||
139 | Some(parent) => expr = parent, | ||
140 | None => break, | ||
141 | } | ||
142 | } | ||
143 | |||
144 | // If it's a statement in a block, we don't know how many statements should be included | ||
145 | if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { | ||
146 | return None; | ||
147 | } | ||
148 | |||
149 | // Insert `}` right after the expression. | ||
150 | Some(TextEdit::insert( | ||
151 | expr.syntax().text_range().end() + TextSize::of("{"), | ||
152 | "}".to_string(), | ||
153 | )) | ||
154 | } | ||
126 | } | 155 | } |
127 | 156 | ||
128 | /// Returns an edit which should be applied after `=` was typed. Primarily, | 157 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
@@ -440,7 +469,7 @@ fn foo() -> { 92 } | |||
440 | } | 469 | } |
441 | 470 | ||
442 | #[test] | 471 | #[test] |
443 | fn adds_closing_brace() { | 472 | fn adds_closing_brace_for_expr() { |
444 | type_char( | 473 | type_char( |
445 | '{', | 474 | '{', |
446 | r#" | 475 | r#" |
@@ -519,4 +548,87 @@ fn f() { | |||
519 | "#, | 548 | "#, |
520 | ); | 549 | ); |
521 | } | 550 | } |
551 | |||
552 | #[test] | ||
553 | fn adds_closing_brace_for_use_tree() { | ||
554 | type_char( | ||
555 | '{', | ||
556 | r#" | ||
557 | use some::$0Path; | ||
558 | "#, | ||
559 | r#" | ||
560 | use some::{Path}; | ||
561 | "#, | ||
562 | ); | ||
563 | type_char( | ||
564 | '{', | ||
565 | r#" | ||
566 | use some::{Path, $0Other}; | ||
567 | "#, | ||
568 | r#" | ||
569 | use some::{Path, {Other}}; | ||
570 | "#, | ||
571 | ); | ||
572 | type_char( | ||
573 | '{', | ||
574 | r#" | ||
575 | use some::{$0Path, Other}; | ||
576 | "#, | ||
577 | r#" | ||
578 | use some::{{Path}, Other}; | ||
579 | "#, | ||
580 | ); | ||
581 | type_char( | ||
582 | '{', | ||
583 | r#" | ||
584 | use some::path::$0to::Item; | ||
585 | "#, | ||
586 | r#" | ||
587 | use some::path::{to::Item}; | ||
588 | "#, | ||
589 | ); | ||
590 | type_char( | ||
591 | '{', | ||
592 | r#" | ||
593 | use some::$0path::to::Item; | ||
594 | "#, | ||
595 | r#" | ||
596 | use some::{path::to::Item}; | ||
597 | "#, | ||
598 | ); | ||
599 | type_char( | ||
600 | '{', | ||
601 | r#" | ||
602 | use $0some::path::to::Item; | ||
603 | "#, | ||
604 | r#" | ||
605 | use {some::path::to::Item}; | ||
606 | "#, | ||
607 | ); | ||
608 | type_char( | ||
609 | '{', | ||
610 | r#" | ||
611 | use some::path::$0to::{Item}; | ||
612 | "#, | ||
613 | r#" | ||
614 | use some::path::{to::{Item}}; | ||
615 | "#, | ||
616 | ); | ||
617 | type_char( | ||
618 | '{', | ||
619 | r#" | ||
620 | use $0Thing as _; | ||
621 | "#, | ||
622 | r#" | ||
623 | use {Thing as _}; | ||
624 | "#, | ||
625 | ); | ||
626 | |||
627 | type_char_noop( | ||
628 | '{', | ||
629 | r#" | ||
630 | use some::pa$0th::to::Item; | ||
631 | "#, | ||
632 | ); | ||
633 | } | ||
522 | } | 634 | } |
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index 7d2db201a..81c4d95b1 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -54,6 +54,14 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
54 | cov_mark::hit!(indent_block_contents); | 54 | cov_mark::hit!(indent_block_contents); |
55 | return Some(edit); | 55 | return Some(edit); |
56 | } | 56 | } |
57 | |||
58 | // Typing enter after the `{` of a use tree list. | ||
59 | if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{')) | ||
60 | .and_then(|list| on_enter_in_use_tree_list(list, position)) | ||
61 | { | ||
62 | cov_mark::hit!(indent_block_contents); | ||
63 | return Some(edit); | ||
64 | } | ||
57 | } | 65 | } |
58 | 66 | ||
59 | None | 67 | None |
@@ -111,6 +119,21 @@ fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<Te | |||
111 | Some(edit) | 119 | Some(edit) |
112 | } | 120 | } |
113 | 121 | ||
122 | fn on_enter_in_use_tree_list(list: ast::UseTreeList, position: FilePosition) -> Option<TextEdit> { | ||
123 | if list.syntax().text().contains_char('\n') { | ||
124 | return None; | ||
125 | } | ||
126 | |||
127 | let indent = IndentLevel::from_node(list.syntax()); | ||
128 | let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1)); | ||
129 | edit.union(TextEdit::insert( | ||
130 | list.r_curly_token()?.text_range().start(), | ||
131 | format!("\n{}", indent), | ||
132 | )) | ||
133 | .ok()?; | ||
134 | Some(edit) | ||
135 | } | ||
136 | |||
114 | fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> { | 137 | fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> { |
115 | let mut node = block.tail_expr().map(|e| e.syntax().clone()); | 138 | let mut node = block.tail_expr().map(|e| e.syntax().clone()); |
116 | 139 | ||
@@ -484,4 +507,96 @@ fn f() {$0 | |||
484 | "#, | 507 | "#, |
485 | ); | 508 | ); |
486 | } | 509 | } |
510 | |||
511 | #[test] | ||
512 | fn indents_use_tree_list() { | ||
513 | do_check( | ||
514 | r#" | ||
515 | use crate::{$0}; | ||
516 | "#, | ||
517 | r#" | ||
518 | use crate::{ | ||
519 | $0 | ||
520 | }; | ||
521 | "#, | ||
522 | ); | ||
523 | do_check( | ||
524 | r#" | ||
525 | use crate::{$0Object, path::to::OtherThing}; | ||
526 | "#, | ||
527 | r#" | ||
528 | use crate::{ | ||
529 | $0Object, path::to::OtherThing | ||
530 | }; | ||
531 | "#, | ||
532 | ); | ||
533 | do_check( | ||
534 | r#" | ||
535 | use {crate::{$0Object, path::to::OtherThing}}; | ||
536 | "#, | ||
537 | r#" | ||
538 | use {crate::{ | ||
539 | $0Object, path::to::OtherThing | ||
540 | }}; | ||
541 | "#, | ||
542 | ); | ||
543 | do_check( | ||
544 | r#" | ||
545 | use { | ||
546 | crate::{$0Object, path::to::OtherThing} | ||
547 | }; | ||
548 | "#, | ||
549 | r#" | ||
550 | use { | ||
551 | crate::{ | ||
552 | $0Object, path::to::OtherThing | ||
553 | } | ||
554 | }; | ||
555 | "#, | ||
556 | ); | ||
557 | } | ||
558 | |||
559 | #[test] | ||
560 | fn does_not_indent_use_tree_list_when_not_at_curly_brace() { | ||
561 | do_check_noop( | ||
562 | r#" | ||
563 | use path::{Thing$0}; | ||
564 | "#, | ||
565 | ); | ||
566 | } | ||
567 | |||
568 | #[test] | ||
569 | fn does_not_indent_use_tree_list_without_curly_braces() { | ||
570 | do_check_noop( | ||
571 | r#" | ||
572 | use path::Thing$0; | ||
573 | "#, | ||
574 | ); | ||
575 | do_check_noop( | ||
576 | r#" | ||
577 | use path::$0Thing; | ||
578 | "#, | ||
579 | ); | ||
580 | do_check_noop( | ||
581 | r#" | ||
582 | use path::Thing$0}; | ||
583 | "#, | ||
584 | ); | ||
585 | do_check_noop( | ||
586 | r#" | ||
587 | use path::{$0Thing; | ||
588 | "#, | ||
589 | ); | ||
590 | } | ||
591 | |||
592 | #[test] | ||
593 | fn does_not_indent_multiline_use_tree_list() { | ||
594 | do_check_noop( | ||
595 | r#" | ||
596 | use path::{$0 | ||
597 | Thing | ||
598 | }; | ||
599 | "#, | ||
600 | ); | ||
601 | } | ||
487 | } | 602 | } |