@@ -20,9 +20,9 @@ use ide_db::{
2020 RootDatabase ,
2121} ;
2222use syntax:: {
23- algo:: find_node_at_offset,
23+ algo:: { ancestors_at_offset , find_node_at_offset} ,
2424 ast:: { self , edit:: IndentLevel , AstToken } ,
25- AstNode , Parse , SourceFile , SyntaxKind , TextRange , TextSize ,
25+ AstNode , Parse , SourceFile , SyntaxKind , TextRange , TextSize , T ,
2626} ;
2727
2828use text_edit:: { Indel , TextEdit } ;
@@ -32,7 +32,7 @@ use crate::SourceChange;
3232pub ( crate ) use on_enter:: on_enter;
3333
3434// 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 = ".=< >{" ;
3636
3737struct ExtendedTextEdit {
3838 edit : TextEdit ,
@@ -92,7 +92,8 @@ fn on_char_typed_inner(
9292 match char_typed {
9393 '.' => conv ( on_dot_typed ( & file. tree ( ) , offset) ) ,
9494 '=' => conv ( on_eq_typed ( & file. tree ( ) , offset) ) ,
95- '>' => conv ( on_arrow_typed ( & file. tree ( ) , offset) ) ,
95+ '<' => on_left_angle_typed ( & file. tree ( ) , offset) ,
96+ '>' => conv ( on_right_angle_typed ( & file. tree ( ) , offset) ) ,
9697 '{' => conv ( on_opening_brace_typed ( file, offset) ) ,
9798 _ => unreachable ! ( ) ,
9899 }
@@ -312,8 +313,40 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
312313 Some ( TextEdit :: replace ( TextRange :: new ( offset - current_indent_len, offset) , target_indent) )
313314}
314315
316+ /// Add closing `>` for generic arguments/parameters.
317+ fn on_left_angle_typed ( file : & SourceFile , offset : TextSize ) -> Option < ExtendedTextEdit > {
318+ let file_text = file. syntax ( ) . text ( ) ;
319+ if !stdx:: always!( file_text. char_at( offset) == Some ( '<' ) ) {
320+ return None ;
321+ }
322+ let range = TextRange :: at ( offset, TextSize :: of ( '<' ) ) ;
323+
324+ if let Some ( t) = file. syntax ( ) . token_at_offset ( offset) . left_biased ( ) {
325+ if T ! [ impl ] == t. kind ( ) {
326+ return Some ( ExtendedTextEdit {
327+ edit : TextEdit :: replace ( range, "<$0>" . to_string ( ) ) ,
328+ is_snippet : true ,
329+ } ) ;
330+ }
331+ }
332+
333+ if ancestors_at_offset ( file. syntax ( ) , offset)
334+ . find ( |n| {
335+ ast:: GenericParamList :: can_cast ( n. kind ( ) ) || ast:: GenericArgList :: can_cast ( n. kind ( ) )
336+ } )
337+ . is_some ( )
338+ {
339+ return Some ( ExtendedTextEdit {
340+ edit : TextEdit :: replace ( range, "<$0>" . to_string ( ) ) ,
341+ is_snippet : true ,
342+ } ) ;
343+ }
344+
345+ None
346+ }
347+
315348/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
316- fn on_arrow_typed ( file : & SourceFile , offset : TextSize ) -> Option < TextEdit > {
349+ fn on_right_angle_typed ( file : & SourceFile , offset : TextSize ) -> Option < TextEdit > {
317350 let file_text = file. syntax ( ) . text ( ) ;
318351 if !stdx:: always!( file_text. char_at( offset) == Some ( '>' ) ) {
319352 return None ;
@@ -335,6 +368,12 @@ mod tests {
335368
336369 use super :: * ;
337370
371+ impl ExtendedTextEdit {
372+ fn apply ( & self , text : & mut String ) {
373+ self . edit . apply ( text) ;
374+ }
375+ }
376+
338377 fn do_type_char ( char_typed : char , before : & str ) -> Option < String > {
339378 let ( offset, mut before) = extract_offset ( before) ;
340379 let edit = TextEdit :: insert ( offset, char_typed. to_string ( ) ) ;
@@ -879,6 +918,169 @@ use some::pa$0th::to::Item;
879918 ) ;
880919 }
881920
921+ #[ test]
922+ fn adds_closing_angle_bracket_for_generic_args ( ) {
923+ type_char (
924+ '<' ,
925+ r#"
926+ fn foo() {
927+ bar::$0
928+ }
929+ "# ,
930+ r#"
931+ fn foo() {
932+ bar::<$0>
933+ }
934+ "# ,
935+ ) ;
936+
937+ type_char (
938+ '<' ,
939+ r#"
940+ fn foo(bar: &[u64]) {
941+ bar.iter().collect::$0();
942+ }
943+ "# ,
944+ r#"
945+ fn foo(bar: &[u64]) {
946+ bar.iter().collect::<$0>();
947+ }
948+ "# ,
949+ ) ;
950+ }
951+
952+ #[ test]
953+ fn adds_closing_angle_bracket_for_generic_params ( ) {
954+ type_char (
955+ '<' ,
956+ r#"
957+ fn foo$0() {}
958+ "# ,
959+ r#"
960+ fn foo<$0>() {}
961+ "# ,
962+ ) ;
963+ type_char (
964+ '<' ,
965+ r#"
966+ fn foo$0
967+ "# ,
968+ r#"
969+ fn foo<$0>
970+ "# ,
971+ ) ;
972+ type_char (
973+ '<' ,
974+ r#"
975+ struct Foo$0 {}
976+ "# ,
977+ r#"
978+ struct Foo<$0> {}
979+ "# ,
980+ ) ;
981+ type_char (
982+ '<' ,
983+ r#"
984+ struct Foo$0();
985+ "# ,
986+ r#"
987+ struct Foo<$0>();
988+ "# ,
989+ ) ;
990+ type_char (
991+ '<' ,
992+ r#"
993+ struct Foo$0
994+ "# ,
995+ r#"
996+ struct Foo<$0>
997+ "# ,
998+ ) ;
999+ type_char (
1000+ '<' ,
1001+ r#"
1002+ enum Foo$0
1003+ "# ,
1004+ r#"
1005+ enum Foo<$0>
1006+ "# ,
1007+ ) ;
1008+ type_char (
1009+ '<' ,
1010+ r#"
1011+ trait Foo$0
1012+ "# ,
1013+ r#"
1014+ trait Foo<$0>
1015+ "# ,
1016+ ) ;
1017+ type_char (
1018+ '<' ,
1019+ r#"
1020+ type Foo$0 = Bar;
1021+ "# ,
1022+ r#"
1023+ type Foo<$0> = Bar;
1024+ "# ,
1025+ ) ;
1026+ type_char (
1027+ '<' ,
1028+ r#"
1029+ impl$0 Foo {}
1030+ "# ,
1031+ r#"
1032+ impl<$0> Foo {}
1033+ "# ,
1034+ ) ;
1035+ type_char (
1036+ '<' ,
1037+ r#"
1038+ impl<T> Foo$0 {}
1039+ "# ,
1040+ r#"
1041+ impl<T> Foo<$0> {}
1042+ "# ,
1043+ ) ;
1044+ type_char (
1045+ '<' ,
1046+ r#"
1047+ impl Foo$0 {}
1048+ "# ,
1049+ r#"
1050+ impl Foo<$0> {}
1051+ "# ,
1052+ ) ;
1053+ }
1054+
1055+ #[ test]
1056+ fn dont_add_closing_angle_bracket_for_comparison ( ) {
1057+ type_char_noop (
1058+ '<' ,
1059+ r#"
1060+ fn main() {
1061+ 42$0
1062+ }
1063+ "# ,
1064+ ) ;
1065+ type_char_noop (
1066+ '<' ,
1067+ r#"
1068+ fn main() {
1069+ 42 $0
1070+ }
1071+ "# ,
1072+ ) ;
1073+ type_char_noop (
1074+ '<' ,
1075+ r#"
1076+ fn main() {
1077+ let foo = 42;
1078+ foo $0
1079+ }
1080+ "# ,
1081+ ) ;
1082+ }
1083+
8821084 #[ test]
8831085 fn regression_629 ( ) {
8841086 type_char_noop (
0 commit comments