@@ -32,7 +32,7 @@ use crate::SourceChange;
32
32
pub ( crate ) use on_enter:: on_enter;
33
33
34
34
// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
35
- pub ( crate ) const TRIGGER_CHARS : & str = ".=<>{" ;
35
+ pub ( crate ) const TRIGGER_CHARS : & str = ".=<>{( " ;
36
36
37
37
struct ExtendedTextEdit {
38
38
edit : TextEdit ,
@@ -91,7 +91,8 @@ fn on_char_typed_inner(
91
91
'=' => conv ( on_eq_typed ( & file. tree ( ) , offset) ) ,
92
92
'<' => on_left_angle_typed ( & file. tree ( ) , offset) ,
93
93
'>' => conv ( on_right_angle_typed ( & file. tree ( ) , offset) ) ,
94
- '{' => conv ( on_opening_brace_typed ( file, offset) ) ,
94
+ '{' => conv ( on_opening_bracket_typed ( file, offset, '{' ) ) ,
95
+ '(' => conv ( on_opening_bracket_typed ( file, offset, '(' ) ) ,
95
96
_ => return None ,
96
97
} ;
97
98
@@ -100,31 +101,43 @@ fn on_char_typed_inner(
100
101
}
101
102
}
102
103
103
- /// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
104
- /// block, or a part of a `use` item.
105
- fn on_opening_brace_typed ( file : & Parse < SourceFile > , offset : TextSize ) -> Option < TextEdit > {
106
- if !stdx:: always!( file. tree( ) . syntax( ) . text( ) . char_at( offset) == Some ( '{' ) ) {
104
+ /// Inserts a closing bracket when the user types an opening bracket, wrapping an existing expression in a
105
+ /// block, or a part of a `use` item (for `{`).
106
+ fn on_opening_bracket_typed (
107
+ file : & Parse < SourceFile > ,
108
+ offset : TextSize ,
109
+ opening_bracket : char ,
110
+ ) -> Option < TextEdit > {
111
+ let ( closing_bracket, expected_ast_bracket) = match opening_bracket {
112
+ '{' => ( '}' , SyntaxKind :: L_CURLY ) ,
113
+ '(' => ( ')' , SyntaxKind :: L_PAREN ) ,
114
+ _ => return None ,
115
+ } ;
116
+
117
+ if !stdx:: always!( file. tree( ) . syntax( ) . text( ) . char_at( offset) == Some ( opening_bracket) ) {
107
118
return None ;
108
119
}
109
120
110
121
let brace_token = file. tree ( ) . syntax ( ) . token_at_offset ( offset) . right_biased ( ) ?;
111
- if brace_token. kind ( ) != SyntaxKind :: L_CURLY {
122
+ if brace_token. kind ( ) != expected_ast_bracket {
112
123
return None ;
113
124
}
114
125
115
- // Remove the `{` to get a better parse tree, and reparse.
126
+ // Remove the opening bracket to get a better parse tree, and reparse.
116
127
let range = brace_token. text_range ( ) ;
117
- if !stdx:: always!( range. len( ) == TextSize :: of( '{' ) ) {
128
+ if !stdx:: always!( range. len( ) == TextSize :: of( opening_bracket ) ) {
118
129
return None ;
119
130
}
120
131
let file = file. reparse ( & Indel :: delete ( range) ) ;
121
132
122
- if let Some ( edit) = brace_expr ( & file. tree ( ) , offset) {
133
+ if let Some ( edit) = bracket_expr ( & file. tree ( ) , offset, opening_bracket , closing_bracket ) {
123
134
return Some ( edit) ;
124
135
}
125
136
126
- if let Some ( edit) = brace_use_path ( & file. tree ( ) , offset) {
127
- return Some ( edit) ;
137
+ if closing_bracket == '}' {
138
+ if let Some ( edit) = brace_use_path ( & file. tree ( ) , offset) {
139
+ return Some ( edit) ;
140
+ }
128
141
}
129
142
130
143
return None ;
@@ -143,7 +156,12 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<
143
156
) )
144
157
}
145
158
146
- fn brace_expr ( file : & SourceFile , offset : TextSize ) -> Option < TextEdit > {
159
+ fn bracket_expr (
160
+ file : & SourceFile ,
161
+ offset : TextSize ,
162
+ opening_bracket : char ,
163
+ closing_bracket : char ,
164
+ ) -> Option < TextEdit > {
147
165
let mut expr: ast:: Expr = find_node_at_offset ( file. syntax ( ) , offset) ?;
148
166
if expr. syntax ( ) . text_range ( ) . start ( ) != offset {
149
167
return None ;
@@ -166,10 +184,10 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<
166
184
return None ;
167
185
}
168
186
169
- // Insert `}` right after the expression.
187
+ // Insert the closing bracket right after the expression.
170
188
Some ( TextEdit :: insert (
171
- expr. syntax ( ) . text_range ( ) . end ( ) + TextSize :: of ( "{" ) ,
172
- "}" . to_string ( ) ,
189
+ expr. syntax ( ) . text_range ( ) . end ( ) + TextSize :: of ( opening_bracket ) ,
190
+ closing_bracket . to_string ( ) ,
173
191
) )
174
192
}
175
193
}
@@ -937,6 +955,193 @@ use some::pa$0th::to::Item;
937
955
) ;
938
956
}
939
957
958
+ #[ test]
959
+ fn adds_closing_parenthesis_for_expr ( ) {
960
+ type_char (
961
+ '(' ,
962
+ r#"
963
+ fn f() { match () { _ => $0() } }
964
+ "# ,
965
+ r#"
966
+ fn f() { match () { _ => (()) } }
967
+ "# ,
968
+ ) ;
969
+ type_char (
970
+ '(' ,
971
+ r#"
972
+ fn f() { $0() }
973
+ "# ,
974
+ r#"
975
+ fn f() { (()) }
976
+ "# ,
977
+ ) ;
978
+ type_char (
979
+ '(' ,
980
+ r#"
981
+ fn f() { let x = $0(); }
982
+ "# ,
983
+ r#"
984
+ fn f() { let x = (()); }
985
+ "# ,
986
+ ) ;
987
+ type_char (
988
+ '(' ,
989
+ r#"
990
+ fn f() { let x = $0a.b(); }
991
+ "# ,
992
+ r#"
993
+ fn f() { let x = (a.b()); }
994
+ "# ,
995
+ ) ;
996
+ type_char (
997
+ '(' ,
998
+ r#"
999
+ const S: () = $0();
1000
+ fn f() {}
1001
+ "# ,
1002
+ r#"
1003
+ const S: () = (());
1004
+ fn f() {}
1005
+ "# ,
1006
+ ) ;
1007
+ type_char (
1008
+ '(' ,
1009
+ r#"
1010
+ const S: () = $0a.b();
1011
+ fn f() {}
1012
+ "# ,
1013
+ r#"
1014
+ const S: () = (a.b());
1015
+ fn f() {}
1016
+ "# ,
1017
+ ) ;
1018
+ type_char (
1019
+ '(' ,
1020
+ r#"
1021
+ fn f() {
1022
+ match x {
1023
+ 0 => $0(),
1024
+ 1 => (),
1025
+ }
1026
+ }
1027
+ "# ,
1028
+ r#"
1029
+ fn f() {
1030
+ match x {
1031
+ 0 => (()),
1032
+ 1 => (),
1033
+ }
1034
+ }
1035
+ "# ,
1036
+ ) ;
1037
+ type_char (
1038
+ '(' ,
1039
+ r#"
1040
+ fn f() {
1041
+ let z = Some($03);
1042
+ }
1043
+ "# ,
1044
+ r#"
1045
+ fn f() {
1046
+ let z = Some((3));
1047
+ }
1048
+ "# ,
1049
+ ) ;
1050
+ }
1051
+
1052
+ #[ test]
1053
+ fn parenthesis_noop_in_string_literal ( ) {
1054
+ // Regression test for #9351
1055
+ type_char_noop (
1056
+ '(' ,
1057
+ r##"
1058
+ fn check_with(ra_fixture: &str, expect: Expect) {
1059
+ let base = r#"
1060
+ enum E { T(), R$0, C }
1061
+ use self::E::X;
1062
+ const Z: E = E::C;
1063
+ mod m {}
1064
+ asdasdasdasdasdasda
1065
+ sdasdasdasdasdasda
1066
+ sdasdasdasdasd
1067
+ "#;
1068
+ let actual = completion_list(&format!("{}\n{}", base, ra_fixture));
1069
+ expect.assert_eq(&actual)
1070
+ }
1071
+ "## ,
1072
+ ) ;
1073
+ }
1074
+
1075
+ #[ test]
1076
+ fn parenthesis_noop_in_item_position_with_macro ( ) {
1077
+ type_char_noop ( '(' , r#"$0println!();"# ) ;
1078
+ type_char_noop (
1079
+ '(' ,
1080
+ r#"
1081
+ fn main() $0println!("hello");
1082
+ }"# ,
1083
+ ) ;
1084
+ }
1085
+
1086
+ #[ test]
1087
+ fn parenthesis_noop_in_use_tree ( ) {
1088
+ type_char_noop (
1089
+ '(' ,
1090
+ r#"
1091
+ use some::$0Path;
1092
+ "# ,
1093
+ ) ;
1094
+ type_char_noop (
1095
+ '(' ,
1096
+ r#"
1097
+ use some::{Path, $0Other};
1098
+ "# ,
1099
+ ) ;
1100
+ type_char_noop (
1101
+ '(' ,
1102
+ r#"
1103
+ use some::{$0Path, Other};
1104
+ "# ,
1105
+ ) ;
1106
+ type_char_noop (
1107
+ '(' ,
1108
+ r#"
1109
+ use some::path::$0to::Item;
1110
+ "# ,
1111
+ ) ;
1112
+ type_char_noop (
1113
+ '(' ,
1114
+ r#"
1115
+ use some::$0path::to::Item;
1116
+ "# ,
1117
+ ) ;
1118
+ type_char_noop (
1119
+ '(' ,
1120
+ r#"
1121
+ use $0some::path::to::Item;
1122
+ "# ,
1123
+ ) ;
1124
+ type_char_noop (
1125
+ '(' ,
1126
+ r#"
1127
+ use some::path::$0to::{Item};
1128
+ "# ,
1129
+ ) ;
1130
+ type_char_noop (
1131
+ '(' ,
1132
+ r#"
1133
+ use $0Thing as _;
1134
+ "# ,
1135
+ ) ;
1136
+
1137
+ type_char_noop (
1138
+ '(' ,
1139
+ r#"
1140
+ use some::pa$0th::to::Item;
1141
+ "# ,
1142
+ ) ;
1143
+ }
1144
+
940
1145
#[ test]
941
1146
fn adds_closing_angle_bracket_for_generic_args ( ) {
942
1147
type_char (
0 commit comments