@@ -41,6 +41,10 @@ fn advertises_full_sync_diagnostics_formatting_and_semantic_tokens() {
4141 response[ 0 ] [ "result" ] [ "capabilities" ] [ "codeActionProvider" ] [ "codeActionKinds" ] [ 0 ] ,
4242 "quickfix"
4343 ) ;
44+ assert_eq ! (
45+ response[ 0 ] [ "result" ] [ "capabilities" ] [ "codeActionProvider" ] [ "codeActionKinds" ] [ 1 ] ,
46+ "refactor.extract"
47+ ) ;
4448 assert_eq ! (
4549 response[ 0 ] [ "result" ] [ "capabilities" ] [ "semanticTokensProvider" ] [ "full" ] ,
4650 true
@@ -949,6 +953,144 @@ fn document_symbols_and_code_actions_preserve_compiler_structure() {
949953 assert ! ( unrelated[ 0 ] [ "result" ] . as_array( ) . unwrap( ) . is_empty( ) ) ;
950954}
951955
956+ #[ test]
957+ fn code_actions_extract_selected_expressions ( ) {
958+ let source = concat ! (
959+ "state \" game.exe\" {}\n " ,
960+ "fn score(offset: i32) {\n " ,
961+ " return offset + 1\n " ,
962+ "}\n "
963+ ) ;
964+ let uri = "file:///refactor.split" ;
965+ let mut server = LanguageServer :: default ( ) ;
966+ initialize ( & mut server) ;
967+ server. handle ( notification (
968+ "textDocument/didOpen" ,
969+ json ! ( {
970+ "textDocument" : {
971+ "uri" : uri,
972+ "version" : 1 ,
973+ "text" : source
974+ }
975+ } ) ,
976+ ) ) ;
977+
978+ let start = source. find ( "offset + 1" ) . unwrap ( ) ;
979+ let end = start + "offset + 1" . len ( ) ;
980+ let ( start_line, start_character) = position_parts ( source, start) ;
981+ let ( end_line, end_character) = position_parts ( source, end) ;
982+ let actions = server. handle ( json ! ( {
983+ "jsonrpc" : "2.0" ,
984+ "id" : 24 ,
985+ "method" : "textDocument/codeAction" ,
986+ "params" : {
987+ "textDocument" : { "uri" : uri } ,
988+ "range" : {
989+ "start" : { "line" : start_line, "character" : start_character } ,
990+ "end" : { "line" : end_line, "character" : end_character }
991+ } ,
992+ "context" : { "diagnostics" : [ ] , "only" : [ "refactor.extract" ] }
993+ }
994+ } ) ) ;
995+ let actions = actions[ 0 ] [ "result" ] . as_array ( ) . unwrap ( ) ;
996+ assert_eq ! ( actions. len( ) , 2 , "{actions:#?}" ) ;
997+ assert_eq ! ( actions[ 0 ] [ "kind" ] , "refactor.extract.variable" ) ;
998+ assert_eq ! ( actions[ 1 ] [ "kind" ] , "refactor.extract.function" ) ;
999+ assert_eq ! (
1000+ actions[ 1 ] [ "edit" ] [ "changes" ] [ uri] [ 0 ] [ "newText" ] ,
1001+ "extracted(offset)"
1002+ ) ;
1003+
1004+ let function_only = server. handle ( json ! ( {
1005+ "jsonrpc" : "2.0" ,
1006+ "id" : 25 ,
1007+ "method" : "textDocument/codeAction" ,
1008+ "params" : {
1009+ "textDocument" : { "uri" : uri } ,
1010+ "range" : {
1011+ "start" : { "line" : start_line, "character" : start_character } ,
1012+ "end" : { "line" : end_line, "character" : end_character }
1013+ } ,
1014+ "context" : {
1015+ "diagnostics" : [ ] ,
1016+ "only" : [ "refactor.extract.function" ]
1017+ }
1018+ }
1019+ } ) ) ;
1020+ let function_only = function_only[ 0 ] [ "result" ] . as_array ( ) . unwrap ( ) ;
1021+ assert_eq ! ( function_only. len( ) , 1 ) ;
1022+ assert_eq ! ( function_only[ 0 ] [ "kind" ] , "refactor.extract.function" ) ;
1023+
1024+ let quick_fixes = server. handle ( json ! ( {
1025+ "jsonrpc" : "2.0" ,
1026+ "id" : 26 ,
1027+ "method" : "textDocument/codeAction" ,
1028+ "params" : {
1029+ "textDocument" : { "uri" : uri } ,
1030+ "range" : {
1031+ "start" : { "line" : start_line, "character" : start_character } ,
1032+ "end" : { "line" : end_line, "character" : end_character }
1033+ } ,
1034+ "context" : { "diagnostics" : [ ] , "only" : [ "quickfix" ] }
1035+ }
1036+ } ) ) ;
1037+ assert ! ( quick_fixes[ 0 ] [ "result" ] . as_array( ) . unwrap( ) . is_empty( ) ) ;
1038+
1039+ let statement_source = concat ! (
1040+ "state \" game.exe\" {}\n " ,
1041+ "fn report(value: i32) {\n " ,
1042+ " print(value)\n " ,
1043+ " print(value + 1)\n " ,
1044+ "}\n "
1045+ ) ;
1046+ let statement_uri = "file:///statement-refactor.split" ;
1047+ server. handle ( notification (
1048+ "textDocument/didOpen" ,
1049+ json ! ( {
1050+ "textDocument" : {
1051+ "uri" : statement_uri,
1052+ "version" : 1 ,
1053+ "text" : statement_source
1054+ }
1055+ } ) ,
1056+ ) ) ;
1057+ let statement_start = statement_source. find ( "print(value)" ) . unwrap ( ) ;
1058+ let statement_end =
1059+ statement_source. find ( "print(value + 1)" ) . unwrap ( ) + "print(value + 1)" . len ( ) ;
1060+ let ( statement_start_line, statement_start_character) =
1061+ position_parts ( statement_source, statement_start) ;
1062+ let ( statement_end_line, statement_end_character) =
1063+ position_parts ( statement_source, statement_end) ;
1064+ let statements = server. handle ( json ! ( {
1065+ "jsonrpc" : "2.0" ,
1066+ "id" : 27 ,
1067+ "method" : "textDocument/codeAction" ,
1068+ "params" : {
1069+ "textDocument" : { "uri" : statement_uri } ,
1070+ "range" : {
1071+ "start" : {
1072+ "line" : statement_start_line,
1073+ "character" : statement_start_character
1074+ } ,
1075+ "end" : {
1076+ "line" : statement_end_line,
1077+ "character" : statement_end_character
1078+ }
1079+ } ,
1080+ "context" : {
1081+ "diagnostics" : [ ] ,
1082+ "only" : [ "refactor.extract.function" ]
1083+ }
1084+ }
1085+ } ) ) ;
1086+ let statements = statements[ 0 ] [ "result" ] . as_array ( ) . unwrap ( ) ;
1087+ assert_eq ! ( statements. len( ) , 1 , "{statements:#?}" ) ;
1088+ assert_eq ! (
1089+ statements[ 0 ] [ "edit" ] [ "changes" ] [ statement_uri] [ 0 ] [ "newText" ] ,
1090+ "extracted(value)"
1091+ ) ;
1092+ }
1093+
9521094#[ test]
9531095fn unused_member_code_actions_apply_validated_multi_edit_suppressions ( ) {
9541096 let source = concat ! (
0 commit comments