From: Henri Holopainen Date: Fri, 20 Oct 2023 03:09:33 +0000 (+0300) Subject: Fix merging implicit multiline strings that have inline comments (#3956) X-Git-Url: https://git.madduck.net/etc/vim.git/commitdiff_plain/882d8795c6ff65c02f2657e596391748d1b6b7f5?ds=sidebyside;hp=9edba85f71d50d12996ef7bda576426362016171 Fix merging implicit multiline strings that have inline comments (#3956) * Fix test behaviour * Add new test cases * Skip merging strings that have inline comments * Don't merge lines with multiline strings with inline comments * Changelog entry * Document implicit multiline string merging rules * Fix PR number --- diff --git a/CHANGES.md b/CHANGES.md index 2a50e45..79b5c60 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,7 +12,7 @@ ### Preview style - +- Fix merging implicit multiline strings that have inline comments (#3956) ### Configuration diff --git a/docs/the_black_code_style/future_style.md b/docs/the_black_code_style/future_style.md index 861bb64..367ff98 100644 --- a/docs/the_black_code_style/future_style.md +++ b/docs/the_black_code_style/future_style.md @@ -160,3 +160,67 @@ MULTILINE = """ foobar """.replace("\n", "") ``` + +Implicit multiline strings are special, because they can have inline comments. Strings +without comments are merged, for example + +```python +s = ( + "An " + "implicit " + "multiline " + "string" +) +``` + +becomes + +```python +s = "An implicit multiline string" +``` + +A comment on any line of the string (or between two string lines) will block the +merging, so + +```python +s = ( + "An " # Important comment concerning just this line + "implicit " + "multiline " + "string" +) +``` + +and + +```python +s = ( + "An " + "implicit " + # Comment in between + "multiline " + "string" +) +``` + +will not be merged. Having the comment after or before the string lines (but still +inside the parens) will merge the string. For example + +```python +s = ( # Top comment + "An " + "implicit " + "multiline " + "string" + # Bottom comment +) +``` + +becomes + +```python +s = ( # Top comment + "An implicit multiline string" + # Bottom comment +) +``` diff --git a/src/black/linegen.py b/src/black/linegen.py index d12ca39..2bfe587 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -587,6 +587,7 @@ def transform_line( or line.contains_unsplittable_type_ignore() ) and not (line.inside_brackets and line.contains_standalone_comments()) + and not line.contains_implicit_multiline_string_with_comments() ): # Only apply basic string preprocessing, since lines shouldn't be split here. if Preview.string_processing in mode: diff --git a/src/black/lines.py b/src/black/lines.py index 48fde88..6acc95e 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -239,6 +239,21 @@ class Line: return False + def contains_implicit_multiline_string_with_comments(self) -> bool: + """Chck if we have an implicit multiline string with comments on the line""" + for leaf_type, leaf_group_iterator in itertools.groupby( + self.leaves, lambda leaf: leaf.type + ): + if leaf_type != token.STRING: + continue + leaf_list = list(leaf_group_iterator) + if len(leaf_list) == 1: + continue + for leaf in leaf_list: + if self.comments_after(leaf): + return True + return False + def contains_uncollapsable_type_comments(self) -> bool: ignored_ids = set() try: diff --git a/src/black/trans.py b/src/black/trans.py index a2bff7f..a3f6467 100644 --- a/src/black/trans.py +++ b/src/black/trans.py @@ -390,7 +390,19 @@ class StringMerger(StringTransformer, CustomSplitMapMixin): and is_valid_index(idx + 1) and LL[idx + 1].type == token.STRING ): - if not is_part_of_annotation(leaf): + # Let's check if the string group contains an inline comment + # If we have a comment inline, we don't merge the strings + contains_comment = False + i = idx + while is_valid_index(i): + if LL[i].type != token.STRING: + break + if line.comments_after(LL[i]): + contains_comment = True + break + i += 1 + + if not is_part_of_annotation(leaf) and not contains_comment: string_indices.append(idx) # Advance to the next non-STRING leaf. diff --git a/tests/data/cases/preview_long_strings__regression.py b/tests/data/cases/preview_long_strings__regression.py index 40d5e74..436157f 100644 --- a/tests/data/cases/preview_long_strings__regression.py +++ b/tests/data/cases/preview_long_strings__regression.py @@ -210,8 +210,8 @@ def foo(): some_tuple = ("some string", "some string" " which should be joined") -some_commented_string = ( - "This string is long but not so long that it needs hahahah toooooo be so greatttt" # This comment gets thrown to the top. +some_commented_string = ( # This comment stays at the top. + "This string is long but not so long that it needs hahahah toooooo be so greatttt" " {} that I just can't think of any more good words to say about it at" " allllllllllll".format("ha") # comments here are fine ) @@ -834,7 +834,7 @@ def foo(): some_tuple = ("some string", "some string which should be joined") -some_commented_string = ( # This comment gets thrown to the top. +some_commented_string = ( # This comment stays at the top. "This string is long but not so long that it needs hahahah toooooo be so greatttt" " {} that I just can't think of any more good words to say about it at" " allllllllllll".format("ha") # comments here are fine diff --git a/tests/data/cases/preview_multiline_strings.py b/tests/data/cases/preview_multiline_strings.py index dec4ef2..3ff6436 100644 --- a/tests/data/cases/preview_multiline_strings.py +++ b/tests/data/cases/preview_multiline_strings.py @@ -157,6 +157,24 @@ Please use `--build-option` instead, `--global-option` is reserved to flags like `--verbose` or `--quiet`. """ +this_will_become_one_line = ( + "a" + "b" + "c" +) + +this_will_stay_on_three_lines = ( + "a" # comment + "b" + "c" +) + +this_will_also_become_one_line = ( # comment + "a" + "b" + "c" +) + # output """cow say""", @@ -357,3 +375,13 @@ msg = f"""The arguments {bad_arguments} were passed in. Please use `--build-option` instead, `--global-option` is reserved to flags like `--verbose` or `--quiet`. """ + +this_will_become_one_line = "abc" + +this_will_stay_on_three_lines = ( + "a" # comment + "b" + "c" +) + +this_will_also_become_one_line = "abc" # comment