From f1c9eb911ef016753b83de7c597b8f6b89a550fe Mon Sep 17 00:00:00 2001 From: David Szotten Date: Sat, 17 Jun 2023 13:10:57 +0100 Subject: [PATCH] basic impl copied from StmtWhile --- .../test/fixtures/ruff/statement/for.py | 34 +++++++ ...r__tests__black_test__bracketmatch_py.snap | 9 +- ...er__tests__black_test__collections_py.snap | 18 ++-- ...tter__tests__black_test__comments3_py.snap | 10 +- ...tter__tests__black_test__comments5_py.snap | 40 +++++--- ...ter__tests__black_test__expression_py.snap | 95 ++++++++++--------- ...tter__tests__black_test__fmtonoff5_py.snap | 10 +- ...atter__tests__black_test__fmtonoff_py.snap | 26 ++--- ...atter__tests__black_test__fmtskip8_py.snap | 14 +-- ...atter__tests__black_test__function_py.snap | 10 +- ...s__black_test__remove_for_brackets_py.snap | 44 ++++++--- ...move_newline_after_code_block_open_py.snap | 22 +++-- ...__tests__ruff_test__statement__for_py.snap | 87 +++++++++++++++++ .../src/statement/stmt_for.rs | 69 +++++++++++++- 14 files changed, 366 insertions(+), 122 deletions(-) create mode 100644 crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/for.py create mode 100644 crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__statement__for_py.snap diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/for.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/for.py new file mode 100644 index 00000000000000..3a96b5390f3fcc --- /dev/null +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/statement/for.py @@ -0,0 +1,34 @@ +for x in y: # trailing test comment + pass # trailing last statement comment + + # trailing for body comment + +# leading else comment + +else: # trailing else comment + pass + + # trailing else body comment + + +for aVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn in anotherVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn: # trailing comment + pass + +else: + ... + +for ( + x, + y, + ) in z: # comment + ... + + +# remove brackets around x,y but keep them around z,w +for (x, y) in (z, w): + ... + + +# type comment +for x in (): # type: int + ... diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap index fd018bb9fae762..6d4085cd2e07cc 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__bracketmatch_py.snap @@ -19,12 +19,12 @@ lambda x=lambda y={1: 3}: y['x':lambda y: {1: 2}]: x ```diff --- Black +++ Ruff -@@ -1,4 +1,3 @@ +@@ -1,4 +1,4 @@ -for ((x in {}) or {})["a"] in x: -- pass ++for NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] in x: + pass -pem_spam = lambda l, spam={"x": 3}: not spam.get(l.strip()) -lambda x=lambda y={1: 3}: y["x" : lambda y: {1: 2}]: x -+NOT_YET_IMPLEMENTED_StmtFor +pem_spam = lambda x: True +lambda x: True ``` @@ -32,7 +32,8 @@ lambda x=lambda y={1: 3}: y['x':lambda y: {1: 2}]: x ## Ruff Output ```py -NOT_YET_IMPLEMENTED_StmtFor +for NOT_IMPLEMENTED_value[NOT_IMPLEMENTED_key] in x: + pass pem_spam = lambda x: True lambda x: True ``` diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap index 6a6c2179d6a132..f297887d85ca36 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__collections_py.snap @@ -84,7 +84,7 @@ if True: ```diff --- Black +++ Ruff -@@ -1,99 +1,52 @@ +@@ -1,61 +1,36 @@ -import core, time, a +NOT_YET_IMPLEMENTED_StmtImport @@ -164,12 +164,10 @@ if True: +NOT_YET_IMPLEMENTED_StmtAssert # looping over a 1-tuple should also not get wrapped --for x in (1,): -- pass --for (x,) in (1,), (2,), (3,): -- pass -+NOT_YET_IMPLEMENTED_StmtFor -+NOT_YET_IMPLEMENTED_StmtFor + for x in (1,): +@@ -63,37 +38,17 @@ + for (x,) in (1,), (2,), (3,): + pass -[ - 1, @@ -253,8 +251,10 @@ y = {NOT_IMPLEMENTED_dict_key: NOT_IMPLEMENTED_dict_value} NOT_YET_IMPLEMENTED_StmtAssert # looping over a 1-tuple should also not get wrapped -NOT_YET_IMPLEMENTED_StmtFor -NOT_YET_IMPLEMENTED_StmtFor +for x in (1,): + pass +for (x,) in (1,), (2,), (3,): + pass [1, 2, 3] diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap index 1d64b8c2862d84..0c05857e261894 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments3_py.snap @@ -61,7 +61,7 @@ def func(): ```diff --- Black +++ Ruff -@@ -3,46 +3,15 @@ +@@ -3,46 +3,17 @@ # %% def func(): @@ -97,7 +97,9 @@ def func(): - ) - # This should be left alone (after) - ) -+ NOT_YET_IMPLEMENTED_StmtFor ++ for exc in exc_value.NOT_IMPLEMENTED_attr: ++ if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: ++ NOT_IMPLEMENTED_call() # everything is fine if the expression isn't nested - traceback.TracebackException.from_exception( @@ -128,7 +130,9 @@ def func(): # Capture each of the exceptions in the MultiError along with each of their causes and contexts if NOT_IMPLEMENTED_call(): embedded = [] - NOT_YET_IMPLEMENTED_StmtFor + for exc in exc_value.NOT_IMPLEMENTED_attr: + if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: + NOT_IMPLEMENTED_call() # everything is fine if the expression isn't nested NOT_IMPLEMENTED_call() diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap index 4cdfd0bf99ad1d..f33cc82b346c11 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__comments5_py.snap @@ -86,7 +86,7 @@ if __name__ == "__main__": ```diff --- Black +++ Ruff -@@ -1,33 +1,20 @@ +@@ -1,6 +1,6 @@ while True: - if something.changed: - do.stuff() # trailing comment @@ -95,38 +95,41 @@ if __name__ == "__main__": # Comment belongs to the `if` block. # This one belongs to the `while` block. - # Should this one, too? I guess so. +@@ -8,26 +8,20 @@ # This one is properly standalone now. -- + -for i in range(100): -- # first we do this ++for i in NOT_IMPLEMENTED_call(): + # first we do this - if i % 33 == 0: - break ++ if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: ++ NOT_YET_IMPLEMENTED_StmtBreak -- # then we do this + # then we do this - print(i) -- # and finally we loop around -+NOT_YET_IMPLEMENTED_StmtFor ++ NOT_IMPLEMENTED_call() + # and finally we loop around -with open(some_temp_file) as f: - data = f.read() -- ++NOT_YET_IMPLEMENTED_StmtWith + -try: - with open(some_other_file) as w: - w.write(data) -+NOT_YET_IMPLEMENTED_StmtWith ++NOT_YET_IMPLEMENTED_StmtTry -except OSError: - print("problems") -+NOT_YET_IMPLEMENTED_StmtTry - +- -import sys +NOT_YET_IMPLEMENTED_StmtImport # leading function comment -@@ -42,7 +29,7 @@ +@@ -42,7 +36,7 @@ # leading 1 @deco1 # leading 2 @@ -135,7 +138,7 @@ if __name__ == "__main__": # leading 3 @deco3 def decorated1(): -@@ -52,7 +39,7 @@ +@@ -52,7 +46,7 @@ # leading 1 @deco1 # leading 2 @@ -144,7 +147,7 @@ if __name__ == "__main__": # leading function comment def decorated1(): ... -@@ -69,5 +56,5 @@ +@@ -69,5 +63,5 @@ ... @@ -167,7 +170,14 @@ while True: # This one is properly standalone now. -NOT_YET_IMPLEMENTED_StmtFor +for i in NOT_IMPLEMENTED_call(): + # first we do this + if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: + NOT_YET_IMPLEMENTED_StmtBreak + + # then we do this + NOT_IMPLEMENTED_call() + # and finally we loop around NOT_YET_IMPLEMENTED_StmtWith diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap index b2cc26d4f29692..7e3d844b25821b 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__expression_py.snap @@ -276,7 +276,7 @@ last_call() Name None True -@@ -7,294 +8,236 @@ +@@ -7,264 +8,234 @@ 1 1.0 1j @@ -629,7 +629,13 @@ last_call() -) -what_is_up_with_those_new_coord_names = (coord_names | set(vars_to_create)) - set( - vars_to_remove --) ++e = NOT_IMPLEMENTED_call() ++f = 1, NOT_YET_IMPLEMENTED_ExprStarred ++g = 1, NOT_YET_IMPLEMENTED_ExprStarred ++what_is_up_with_those_new_coord_names = ( ++ (coord_names + NOT_IMPLEMENTED_call()) ++ + NOT_IMPLEMENTED_call() + ) -result = ( - session.query(models.Customer.id) - .filter( @@ -637,12 +643,9 @@ last_call() - ) - .order_by(models.Customer.id.asc()) - .all() -+e = NOT_IMPLEMENTED_call() -+f = 1, NOT_YET_IMPLEMENTED_ExprStarred -+g = 1, NOT_YET_IMPLEMENTED_ExprStarred +what_is_up_with_those_new_coord_names = ( -+ (coord_names + NOT_IMPLEMENTED_call()) -+ + NOT_IMPLEMENTED_call() ++ (coord_names | NOT_IMPLEMENTED_call()) ++ - NOT_IMPLEMENTED_call() ) -result = ( - session.query(models.Customer.id) @@ -653,10 +656,7 @@ last_call() - models.Customer.id.asc(), - ) - .all() -+what_is_up_with_those_new_coord_names = ( -+ (coord_names | NOT_IMPLEMENTED_call()) -+ - NOT_IMPLEMENTED_call() - ) +-) -Ø = set() -authors.łukasz.say_thanks() -mapping = { @@ -696,38 +696,36 @@ last_call() - force=False -), "Short message" -assert parens is TooMany --for (x,) in (1,), (2,), (3,): -- ... --for y in (): -- ... --for z in (i for i in (1, 2, 3)): -- ... --for i in call(): -- ... --for j in 1 + (2 + 3): -- ... --while this and that: +NOT_IMPLEMENTED_call() +NOT_IMPLEMENTED_call() +NOT_IMPLEMENTED_call() +NOT_YET_IMPLEMENTED_StmtAssert +NOT_YET_IMPLEMENTED_StmtAssert +NOT_YET_IMPLEMENTED_StmtAssert -+NOT_YET_IMPLEMENTED_StmtFor -+NOT_YET_IMPLEMENTED_StmtFor -+NOT_YET_IMPLEMENTED_StmtFor -+NOT_YET_IMPLEMENTED_StmtFor -+NOT_YET_IMPLEMENTED_StmtFor + for (x,) in (1,), (2,), (3,): + ... + for y in (): + ... +-for z in (i for i in (1, 2, 3)): ++for z in (i for i in []): + ... +-for i in call(): ++for i in NOT_IMPLEMENTED_call(): + ... + for j in 1 + (2 + 3): + ... +-while this and that: +while NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2: ... --for ( -- addr_family, -- addr_type, -- addr_proto, -- addr_canonname, -- addr_sockaddr, + for ( + addr_family, +@@ -272,29 +243,13 @@ + addr_proto, + addr_canonname, + addr_sockaddr, -) in socket.getaddrinfo("google.com", "http"): -- pass ++) in NOT_IMPLEMENTED_call(): + pass -a = ( - aaaa.bbbb.cccc.dddd.eeee.ffff.gggg.hhhh.iiii.jjjj.kkkk.llll.mmmm.nnnn.oooo.pppp - in qqqq.rrrr.ssss.tttt.uuuu.vvvv.xxxx.yyyy.zzzz @@ -749,7 +747,6 @@ last_call() - and threading.current_thread() != threading.main_thread() - or signal.getsignal(signal.SIGINT) != signal.default_int_handler -): -+NOT_YET_IMPLEMENTED_StmtFor +a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right +a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right +a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right @@ -758,7 +755,7 @@ last_call() return True if ( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -@@ -327,24 +270,44 @@ +@@ -327,24 +282,44 @@ ): return True if ( @@ -815,7 +812,7 @@ last_call() ): return True ( -@@ -363,8 +326,9 @@ +@@ -363,8 +338,9 @@ bbbb >> bbbb * bbbb ( aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -1059,14 +1056,26 @@ NOT_IMPLEMENTED_call() NOT_YET_IMPLEMENTED_StmtAssert NOT_YET_IMPLEMENTED_StmtAssert NOT_YET_IMPLEMENTED_StmtAssert -NOT_YET_IMPLEMENTED_StmtFor -NOT_YET_IMPLEMENTED_StmtFor -NOT_YET_IMPLEMENTED_StmtFor -NOT_YET_IMPLEMENTED_StmtFor -NOT_YET_IMPLEMENTED_StmtFor +for (x,) in (1,), (2,), (3,): + ... +for y in (): + ... +for z in (i for i in []): + ... +for i in NOT_IMPLEMENTED_call(): + ... +for j in 1 + (2 + 3): + ... while NOT_IMPLEMENTED_bool_op1 and NOT_IMPLEMENTED_bool_op2: ... -NOT_YET_IMPLEMENTED_StmtFor +for ( + addr_family, + addr_type, + addr_proto, + addr_canonname, + addr_sockaddr, +) in NOT_IMPLEMENTED_call(): + pass a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right a = NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap index 7582c7cdee7e0a..aada9db6c5ad44 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff5_py.snap @@ -134,7 +134,7 @@ elif unformatted: return True # yapf: enable elif b: -@@ -39,49 +21,27 @@ +@@ -39,49 +21,29 @@ # Regression test for https://github.com/psf/black/issues/2567. if True: # fmt: off @@ -142,7 +142,9 @@ elif unformatted: - # fmt: on - print ( "This won't be formatted" ) - print ( "This won't be formatted either" ) -+ NOT_YET_IMPLEMENTED_StmtFor ++ for _ in NOT_IMPLEMENTED_call(): ++ # fmt: on ++ NOT_IMPLEMENTED_call() + NOT_IMPLEMENTED_call() else: - print("This will be formatted") @@ -220,7 +222,9 @@ def test_func(): # Regression test for https://github.com/psf/black/issues/2567. if True: # fmt: off - NOT_YET_IMPLEMENTED_StmtFor + for _ in NOT_IMPLEMENTED_call(): + # fmt: on + NOT_IMPLEMENTED_call() NOT_IMPLEMENTED_call() else: NOT_IMPLEMENTED_call() diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap index 183ed478e1baea..3f5982027bc12a 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtonoff_py.snap @@ -222,7 +222,7 @@ d={'a':1, # Comment 1 # Comment 2 -@@ -18,109 +16,112 @@ +@@ -18,109 +16,114 @@ # fmt: off def func_no_args(): @@ -241,7 +241,9 @@ d={'a':1, + NOT_YET_IMPLEMENTED_StmtRaise + if False: + ... -+ NOT_YET_IMPLEMENTED_StmtFor ++ for i in NOT_IMPLEMENTED_call(): ++ NOT_IMPLEMENTED_call() ++ NOT_YET_IMPLEMENTED_StmtContinue + NOT_IMPLEMENTED_call() + return None + @@ -394,7 +396,7 @@ d={'a':1, # fmt: off # hey, that won't work -@@ -130,13 +131,15 @@ +@@ -130,13 +133,15 @@ def on_and_off_broken(): @@ -415,7 +417,7 @@ d={'a':1, # fmt: on # fmt: off # ...but comments still get reformatted even though they should not be -@@ -145,80 +148,21 @@ +@@ -145,80 +150,21 @@ def long_lines(): if True: @@ -427,11 +429,13 @@ d={'a':1, - implicit_default=True, - ) - ) -- # fmt: off ++ NOT_IMPLEMENTED_call() + # fmt: off - a = ( - unnecessary_bracket() - ) -- # fmt: on ++ a = NOT_IMPLEMENTED_call() + # fmt: on - _type_comment_re = re.compile( - r""" - ^ @@ -452,11 +456,9 @@ d={'a':1, - ) - $ - """, -+ NOT_IMPLEMENTED_call() - # fmt: off +- # fmt: off - re.MULTILINE|re.VERBOSE -+ a = NOT_IMPLEMENTED_call() - # fmt: on +- # fmt: on - ) + _type_comment_re = NOT_IMPLEMENTED_call() @@ -534,7 +536,9 @@ def func_no_args(): NOT_YET_IMPLEMENTED_StmtRaise if False: ... - NOT_YET_IMPLEMENTED_StmtFor + for i in NOT_IMPLEMENTED_call(): + NOT_IMPLEMENTED_call() + NOT_YET_IMPLEMENTED_StmtContinue NOT_IMPLEMENTED_call() return None diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap index f8d1d415c35941..eb9f51311f606e 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__fmtskip8_py.snap @@ -75,7 +75,7 @@ async def test_async_with(): ```diff --- Black +++ Ruff -@@ -1,62 +1,46 @@ +@@ -1,62 +1,47 @@ # Make sure a leading comment is not removed. -def some_func( unformatted, args ): # fmt: skip - print("I am some_func") @@ -99,13 +99,13 @@ async def test_async_with(): - def some_method( self, unformatted, args ): # fmt: skip - print("I am some_method") - return 0 -- ++NOT_YET_IMPLEMENTED_StmtClassDef + - async def some_async_method( self, unformatted, args ): # fmt: skip - print("I am some_async_method") - await asyncio.sleep(1) -+NOT_YET_IMPLEMENTED_StmtClassDef - +- # Make sure a leading comment is not removed. -if unformatted_call( args ): # fmt: skip - print("First branch") @@ -130,7 +130,8 @@ async def test_async_with(): -for i in some_iter( unformatted, args ): # fmt: skip - print("Do something") -+NOT_YET_IMPLEMENTED_StmtFor # fmt: skip ++for i in NOT_IMPLEMENTED_call(): # fmt: skip ++ NOT_IMPLEMENTED_call() async def test_async_for(): @@ -193,7 +194,8 @@ while NOT_IMPLEMENTED_call(): # fmt: skip NOT_IMPLEMENTED_call() -NOT_YET_IMPLEMENTED_StmtFor # fmt: skip +for i in NOT_IMPLEMENTED_call(): # fmt: skip + NOT_IMPLEMENTED_call() async def test_async_for(): diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap index 9f5f58ae760ba5..3310ceaddf60c3 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__function_py.snap @@ -126,7 +126,7 @@ def __await__(): return (yield) def func_no_args(): -@@ -14,135 +13,87 @@ +@@ -14,135 +13,89 @@ b c if True: @@ -138,7 +138,9 @@ def __await__(): return (yield) - print(i) - continue - exec("new-style exec", {}, {}) -+ NOT_YET_IMPLEMENTED_StmtFor ++ for i in NOT_IMPLEMENTED_call(): ++ NOT_IMPLEMENTED_call() ++ NOT_YET_IMPLEMENTED_StmtContinue + NOT_IMPLEMENTED_call() return None @@ -322,7 +324,9 @@ def func_no_args(): NOT_YET_IMPLEMENTED_StmtRaise if False: ... - NOT_YET_IMPLEMENTED_StmtFor + for i in NOT_IMPLEMENTED_call(): + NOT_IMPLEMENTED_call() + NOT_YET_IMPLEMENTED_StmtContinue NOT_IMPLEMENTED_call() return None diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap index dbdfdc6508a8af..465bfb265d46ba 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_for_brackets_py.snap @@ -32,25 +32,28 @@ for (((((k, v))))) in d.items(): ```diff --- Black +++ Ruff -@@ -1,27 +1,13 @@ +@@ -1,27 +1,22 @@ # Only remove tuple brackets after `for` -for k, v in d.items(): - print(k, v) -+NOT_YET_IMPLEMENTED_StmtFor ++for (k, v) in NOT_IMPLEMENTED_call(): ++ NOT_IMPLEMENTED_call() # Don't touch tuple brackets after `in` --for module in (core, _unicodefun): + for module in (core, _unicodefun): - if hasattr(module, "_verify_python3_env"): - module._verify_python3_env = lambda: None -+NOT_YET_IMPLEMENTED_StmtFor ++ if NOT_IMPLEMENTED_call(): ++ module.NOT_IMPLEMENTED_attr = lambda x: True # Brackets remain for long for loop lines --for ( -- why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, -- i_dont_know_but_we_should_still_check_the_behaviour_if_they_do, + for ( + why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, + i_dont_know_but_we_should_still_check_the_behaviour_if_they_do, -) in d.items(): - print(k, v) -+NOT_YET_IMPLEMENTED_StmtFor ++) in NOT_IMPLEMENTED_call(): ++ NOT_IMPLEMENTED_call() -for ( - k, @@ -59,30 +62,41 @@ for (((((k, v))))) in d.items(): - dfkasdjfldsjflkdsjflkdsjfdslkfjldsjfgkjdshgkljjdsfldgkhsdofudsfudsofajdslkfjdslkfjldisfjdffjsdlkfjdlkjjkdflskadjldkfjsalkfjdasj.items() -): - print(k, v) -+NOT_YET_IMPLEMENTED_StmtFor ++for (k, v) in NOT_IMPLEMENTED_call(): ++ NOT_IMPLEMENTED_call() # Test deeply nested brackets -for k, v in d.items(): - print(k, v) -+NOT_YET_IMPLEMENTED_StmtFor ++for (k, v) in NOT_IMPLEMENTED_call(): ++ NOT_IMPLEMENTED_call() ``` ## Ruff Output ```py # Only remove tuple brackets after `for` -NOT_YET_IMPLEMENTED_StmtFor +for (k, v) in NOT_IMPLEMENTED_call(): + NOT_IMPLEMENTED_call() # Don't touch tuple brackets after `in` -NOT_YET_IMPLEMENTED_StmtFor +for module in (core, _unicodefun): + if NOT_IMPLEMENTED_call(): + module.NOT_IMPLEMENTED_attr = lambda x: True # Brackets remain for long for loop lines -NOT_YET_IMPLEMENTED_StmtFor +for ( + why_would_anyone_choose_to_name_a_loop_variable_with_a_name_this_long, + i_dont_know_but_we_should_still_check_the_behaviour_if_they_do, +) in NOT_IMPLEMENTED_call(): + NOT_IMPLEMENTED_call() -NOT_YET_IMPLEMENTED_StmtFor +for (k, v) in NOT_IMPLEMENTED_call(): + NOT_IMPLEMENTED_call() # Test deeply nested brackets -NOT_YET_IMPLEMENTED_StmtFor +for (k, v) in NOT_IMPLEMENTED_call(): + NOT_IMPLEMENTED_call() ``` ## Black Output diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap index 79f86d34781c6f..642e62ce922ed3 100644 --- a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__black_test__remove_newline_after_code_block_open_py.snap @@ -121,7 +121,7 @@ with open("/path/to/file.txt", mode="r") as read_file: ```diff --- Black +++ Ruff -@@ -1,78 +1,68 @@ +@@ -1,78 +1,72 @@ -import random +NOT_YET_IMPLEMENTED_StmtImport @@ -159,18 +159,22 @@ with open("/path/to/file.txt", mode="r") as read_file: -for i in range(5): - print(f"{i}) The line above me should be removed!") -+NOT_YET_IMPLEMENTED_StmtFor ++for i in NOT_IMPLEMENTED_call(): ++ NOT_IMPLEMENTED_call() -for i in range(5): - print(f"{i}) The lines above me should be removed!") -+NOT_YET_IMPLEMENTED_StmtFor ++for i in NOT_IMPLEMENTED_call(): ++ NOT_IMPLEMENTED_call() -for i in range(5): - for j in range(7): - print(f"{i}) The lines above me should be removed!") -+NOT_YET_IMPLEMENTED_StmtFor ++for i in NOT_IMPLEMENTED_call(): ++ for j in NOT_IMPLEMENTED_call(): ++ NOT_IMPLEMENTED_call() -if random.randint(0, 3) == 0: @@ -254,13 +258,17 @@ def foo4(): NOT_YET_IMPLEMENTED_StmtClassDef -NOT_YET_IMPLEMENTED_StmtFor +for i in NOT_IMPLEMENTED_call(): + NOT_IMPLEMENTED_call() -NOT_YET_IMPLEMENTED_StmtFor +for i in NOT_IMPLEMENTED_call(): + NOT_IMPLEMENTED_call() -NOT_YET_IMPLEMENTED_StmtFor +for i in NOT_IMPLEMENTED_call(): + for j in NOT_IMPLEMENTED_call(): + NOT_IMPLEMENTED_call() if NOT_IMPLEMENTED_left < NOT_IMPLEMENTED_right: diff --git a/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__statement__for_py.snap b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__statement__for_py.snap new file mode 100644 index 00000000000000..befc6a42f378da --- /dev/null +++ b/crates/ruff_python_formatter/src/snapshots/ruff_python_formatter__tests__ruff_test__statement__for_py.snap @@ -0,0 +1,87 @@ +--- +source: crates/ruff_python_formatter/src/lib.rs +expression: snapshot +--- +## Input +```py +for x in y: # trailing test comment + pass # trailing last statement comment + + # trailing for body comment + +# leading else comment + +else: # trailing else comment + pass + + # trailing else body comment + + +for aVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn in anotherVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn: # trailing comment + pass + +else: + ... + +for ( + x, + y, + ) in z: # comment + ... + + +# remove brackets around x,y but keep them around z,w +for (x, y) in (z, w): + ... + + +# type comment +for x in (): # type: int + ... +``` + + + +## Output +```py +for x in y: # trailing test comment + pass # trailing last statement comment + + # trailing for body comment + +# leading else comment + +else: # trailing else comment + pass + + # trailing else body comment + + +for ( + aVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn +) in ( + anotherVeryLongNameThatSpillsOverToTheNextLineBecauseItIsExtremelyLongAndGoesOnAndOnAndOnAndOnAndOnAndOnAndOnAndOnAndOn +): # trailing comment + pass + +else: + ... + +for ( + x, + y, +) in z: # comment + ... + + +# remove brackets around x,y but keep them around z,w +for (x, y) in (z, w): + ... + + +# type comment +for x in (): # type: int + ... +``` + + diff --git a/crates/ruff_python_formatter/src/statement/stmt_for.rs b/crates/ruff_python_formatter/src/statement/stmt_for.rs index b943de7cc13786..d0db3cf01fd298 100644 --- a/crates/ruff_python_formatter/src/statement/stmt_for.rs +++ b/crates/ruff_python_formatter/src/statement/stmt_for.rs @@ -1,12 +1,75 @@ -use crate::{not_yet_implemented, FormatNodeRule, PyFormatter}; +use crate::comments::{leading_alternate_branch_comments, trailing_comments}; +use crate::expression::parentheses::Parenthesize; +use crate::prelude::*; +use crate::{FormatNodeRule, PyFormatter}; use ruff_formatter::{write, Buffer, FormatResult}; -use rustpython_parser::ast::StmtFor; +use ruff_python_ast::node::AstNode; +use rustpython_parser::ast::{Ranged, Stmt, StmtFor}; #[derive(Default)] pub struct FormatStmtFor; impl FormatNodeRule for FormatStmtFor { fn fmt_fields(&self, item: &StmtFor, f: &mut PyFormatter) -> FormatResult<()> { - write!(f, [not_yet_implemented(item)]) + let StmtFor { + range: _, + target, + iter, + body, + orelse, + type_comment: _, + } = item; + + let comments = f.context().comments().clone(); + let dangling_comments = comments.dangling_comments(item.as_any_node_ref()); + let body_start = body.first().map_or(iter.end(), Stmt::start); + let or_else_comments_start = + dangling_comments.partition_point(|comment| comment.slice().end() < body_start); + + let (trailing_condition_comments, or_else_comments) = + dangling_comments.split_at(or_else_comments_start); + + write!( + f, + [ + text("for"), + space(), + // TODO: the `IfBreaks` is currently ignored by + // https://github.com/astral-sh/ruff/blob/4b9b6829dccabdd4faf6efa6a118b4868347a701/crates/ruff_python_formatter/src/expression/expr_tuple.rs#L78 + target.format().with_options(Parenthesize::IfBreaks), + space(), + text("in"), + space(), + iter.format().with_options(Parenthesize::IfBreaks), + text(":"), + trailing_comments(trailing_condition_comments), + block_indent(&body.format()) + ] + )?; + + if !orelse.is_empty() { + // Split between leading comments before the `else` keyword and end of line comments at the end of + // the `else:` line. + let trailing_start = + or_else_comments.partition_point(|comment| comment.position().is_own_line()); + let (leading, trailing) = or_else_comments.split_at(trailing_start); + + write!( + f, + [ + leading_alternate_branch_comments(leading, body.last()), + text("else:"), + trailing_comments(trailing), + block_indent(&orelse.format()) + ] + )?; + } + + Ok(()) + } + + fn fmt_dangling_comments(&self, _node: &StmtFor, _f: &mut PyFormatter) -> FormatResult<()> { + // Handled in `fmt_fields` + Ok(()) } }