@@ -17,6 +17,7 @@ use crate::state::lsp::LocalRefactorCodeAction;
1717use crate :: state:: require:: Require ;
1818use crate :: state:: state:: State ;
1919use crate :: test:: util:: get_batched_lsp_operations_report_allow_error;
20+ use crate :: test:: util:: mk_multi_file_state;
2021use crate :: test:: util:: mk_multi_file_state_assert_no_errors;
2122
2223fn apply_patch ( info : & ModuleInfo , range : TextRange , patch : String ) -> ( String , String ) {
@@ -115,7 +116,10 @@ fn find_nth_range(source: &str, needle: &str, occurrence: usize) -> TextRange {
115116 }
116117 start = abs + needle. len ( ) ;
117118 }
118- panic ! ( "missing selection marker in invert-boolean test" ) ;
119+ panic ! (
120+ "could not find occurrence {} of '{}' in source" ,
121+ occurrence, needle
122+ ) ;
119123}
120124
121125fn compute_extract_actions (
@@ -211,6 +215,36 @@ fn assert_no_invert_boolean_action(code: &str, selection: TextRange) {
211215 ) ;
212216}
213217
218+ fn compute_invert_boolean_actions_allow_errors (
219+ code : & str ,
220+ selection : TextRange ,
221+ ) -> (
222+ ModuleInfo ,
223+ Vec < Vec < ( Module , TextRange , String ) > > ,
224+ Vec < String > ,
225+ ) {
226+ let ( handles, state) = mk_multi_file_state ( & [ ( "main" , code) ] , Require :: Everything , false ) ;
227+ let handle = handles. get ( "main" ) . unwrap ( ) ;
228+ let transaction = state. transaction ( ) ;
229+ let module_info = transaction. get_module_info ( handle) . unwrap ( ) ;
230+ let actions = transaction
231+ . invert_boolean_code_actions ( handle, selection)
232+ . unwrap_or_default ( ) ;
233+ let edit_sets: Vec < Vec < ( Module , TextRange , String ) > > =
234+ actions. iter ( ) . map ( |action| action. edits . clone ( ) ) . collect ( ) ;
235+ let titles = actions. iter ( ) . map ( |action| action. title . clone ( ) ) . collect ( ) ;
236+ ( module_info, edit_sets, titles)
237+ }
238+
239+ fn assert_no_invert_boolean_action_allow_errors ( code : & str , selection : TextRange ) {
240+ let ( _, actions, _) = compute_invert_boolean_actions_allow_errors ( code, selection) ;
241+ assert ! (
242+ actions. is_empty( ) ,
243+ "expected no invert-boolean actions, found {}" ,
244+ actions. len( )
245+ ) ;
246+ }
247+
214248fn assert_no_extract_variable_action ( code : & str ) {
215249 let ( _, actions, _) = compute_extract_variable_actions ( code) ;
216250 assert ! (
@@ -911,6 +945,94 @@ def foo():
911945 assert_no_invert_boolean_action ( code, selection) ;
912946}
913947
948+ #[ test]
949+ fn invert_boolean_annotated_assignment ( ) {
950+ let code = r#"
951+ def foo():
952+ abc: bool = True
953+ return abc
954+ "# ;
955+ let selection = find_nth_range ( code, "abc" , 2 ) ;
956+ let updated =
957+ apply_first_invert_boolean_action ( code, selection) . expect ( "expected invert-boolean action" ) ;
958+ let expected = r#"
959+ def foo():
960+ abc: bool = False
961+ return (not abc)
962+ "# ;
963+ assert_eq ! ( expected. trim( ) , updated. trim( ) ) ;
964+ }
965+
966+ #[ test]
967+ fn invert_boolean_rejects_deleted_variable ( ) {
968+ let code = r#"
969+ def foo():
970+ abc = True
971+ del abc
972+ return abc
973+ "# ;
974+ let selection = find_nth_range ( code, "abc" , 3 ) ;
975+ assert_no_invert_boolean_action_allow_errors ( code, selection) ;
976+ }
977+
978+ #[ test]
979+ fn invert_boolean_multiple_assignments ( ) {
980+ let code = r#"
981+ def foo():
982+ abc = True
983+ abc = False
984+ return abc
985+ "# ;
986+ let selection = find_nth_range ( code, "abc" , 3 ) ;
987+ let updated =
988+ apply_first_invert_boolean_action ( code, selection) . expect ( "expected invert-boolean action" ) ;
989+ let expected = r#"
990+ def foo():
991+ abc = True
992+ abc = True
993+ return (not abc)
994+ "# ;
995+ assert_eq ! ( expected. trim( ) , updated. trim( ) ) ;
996+ }
997+
998+ #[ test]
999+ fn invert_boolean_nested_expression_keeps_outer_not ( ) {
1000+ let code = r#"
1001+ def foo():
1002+ abc = True
1003+ other = True
1004+ return not (abc and other)
1005+ "# ;
1006+ let selection = find_nth_range ( code, "abc" , 2 ) ;
1007+ let updated =
1008+ apply_first_invert_boolean_action ( code, selection) . expect ( "expected invert-boolean action" ) ;
1009+ let expected = r#"
1010+ def foo():
1011+ abc = False
1012+ other = True
1013+ return not ((not abc) and other)
1014+ "# ;
1015+ assert_eq ! ( expected. trim( ) , updated. trim( ) ) ;
1016+ }
1017+
1018+ #[ test]
1019+ fn invert_boolean_inverts_unary_not_assignment_value ( ) {
1020+ let code = r#"
1021+ def foo(other_var):
1022+ abc = not other_var
1023+ return abc
1024+ "# ;
1025+ let selection = find_nth_range ( code, "abc" , 2 ) ;
1026+ let updated =
1027+ apply_first_invert_boolean_action ( code, selection) . expect ( "expected invert-boolean action" ) ;
1028+ let expected = r#"
1029+ def foo(other_var):
1030+ abc = other_var
1031+ return (not abc)
1032+ "# ;
1033+ assert_eq ! ( expected. trim( ) , updated. trim( ) ) ;
1034+ }
1035+
9141036#[ test]
9151037fn pull_member_up_basic ( ) {
9161038 let code = r#"
0 commit comments