From 471babd304b0dc89ac9ba5a4736e89880bc93940 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Tue, 3 May 2022 21:36:13 +0100 Subject: [PATCH 01/18] Check line length of last line of docstring to decide whether or not to put closing quotes on next line --- src/black/linegen.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 91fdeef8f2f..8bdda3fdcbd 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -305,9 +305,9 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: quote_len = 1 if docstring[1] != quote_char else 3 docstring = docstring[quote_len:-quote_len] docstring_started_empty = not docstring + indent = " " * 4 * self.current_line.depth if is_multiline_string(leaf): - indent = " " * 4 * self.current_line.depth docstring = fix_docstring(docstring, indent) else: docstring = docstring.strip() @@ -329,7 +329,21 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: # We could enforce triple quotes at this point. quote = quote_char * quote_len - leaf.value = prefix + quote + docstring + quote + + # Make the docstring apart from the closing quotes, which happen below + docstring = prefix + quote + docstring + + # Put closing quotes on new line if max line length exceeded + last_line_length = len(docstring.splitlines()[-1]) + if ( + len(indent) + len(prefix) + 2 * len(quote) + last_line_length + <= self.mode.line_length + ): + docstring += quote + else: + docstring += "\n" + indent + quote + + leaf.value = docstring yield from self.visit_default(leaf) From 0cd15a117e175dec4313facbd28ab5ed1d539164 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Tue, 3 May 2022 22:02:02 +0100 Subject: [PATCH 02/18] Fixing bugs. last_line_length must be 0 for empty strings and we need to find last_line_length before beginning construction of final string --- src/black/linegen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 8bdda3fdcbd..f7d6e14b2d5 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -330,11 +330,11 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: # We could enforce triple quotes at this point. quote = quote_char * quote_len + # Put closing quotes on new line if max line length exceeded + last_line_length = len(docstring.splitlines()[-1]) if docstring else 0 + # Make the docstring apart from the closing quotes, which happen below docstring = prefix + quote + docstring - - # Put closing quotes on new line if max line length exceeded - last_line_length = len(docstring.splitlines()[-1]) if ( len(indent) + len(prefix) + 2 * len(quote) + last_line_length <= self.mode.line_length From 6413776d7c1bd8c952428f09adec0f016195d290 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Tue, 3 May 2022 22:19:01 +0100 Subject: [PATCH 03/18] updating change log --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 4cea7fceaad..29a443da5b9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,8 @@ ## Unreleased +- Fixed bug where docstrings with triple quotes could exceed max line length (#3044) + ### Highlights From 3d27415ae9ee4f8d72f014762f182b736453ecfc Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 16:18:56 +0100 Subject: [PATCH 04/18] Moving change to preview style section --- CHANGES.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 29a443da5b9..8f43431c842 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,8 +2,6 @@ ## Unreleased -- Fixed bug where docstrings with triple quotes could exceed max line length (#3044) - ### Highlights @@ -19,6 +17,7 @@ +- Fixed bug where docstrings with triple quotes could exceed max line length (#3044) - Remove redundant parentheses around awaited objects (#2991) - Parentheses around return annotations are now managed (#2990) - Remove unnecessary parentheses from `with` statements (#2926) From e618a5a16d837990014b5e79fe5907a6c232bfa5 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 16:21:53 +0100 Subject: [PATCH 05/18] Adding to Preview --- src/black/mode.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/black/mode.py b/src/black/mode.py index 6bd4ce14421..a418e0eb665 100644 --- a/src/black/mode.py +++ b/src/black/mode.py @@ -147,6 +147,7 @@ class Preview(Enum): remove_redundant_parens = auto() one_element_subscript = auto() annotation_parens = auto() + long_docstring_quotes_on_newline = auto() class Deprecated(UserWarning): From bbf6053d031237e02942616452dc9736b0418b43 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 19:46:42 +0100 Subject: [PATCH 06/18] added tests for docstrings that go over line limit and one at the line limit --- tests/data/docstring.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/data/docstring.py b/tests/data/docstring.py index 96bcf525b16..91d292552ac 100644 --- a/tests/data/docstring.py +++ b/tests/data/docstring.py @@ -188,6 +188,22 @@ def my_god_its_full_of_stars_2(): "I'm sorry Dave " +def docstring_almost_at_line_limit(): + """long docstring................................................................. + """ + + +def docstring_almost_at_line_limit2(): + """long docstring................................................................. + + .................................................................................. + """ + + +def docstring_at_line_limit(): + """long docstring................................................................""" + + # output class MyClass: @@ -375,3 +391,19 @@ def my_god_its_full_of_stars_1(): # the space below is actually a \u2001, removed in output def my_god_its_full_of_stars_2(): "I'm sorry Dave" + + +def docstring_almost_at_line_limit(): + """long docstring................................................................. + """ + + +def docstring_almost_at_line_limit2(): + """long docstring................................................................. + + .................................................................................. + """ + + +def docstring_at_line_limit(): + """long docstring................................................................""" From 07fc574d59f4f5f1d0e4edbc501e76dcca16abc8 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 20:38:50 +0100 Subject: [PATCH 07/18] Converting to preview --- src/black/linegen.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index f7d6e14b2d5..b07e8c34ddd 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -330,20 +330,23 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: # We could enforce triple quotes at this point. quote = quote_char * quote_len - # Put closing quotes on new line if max line length exceeded - last_line_length = len(docstring.splitlines()[-1]) if docstring else 0 - - # Make the docstring apart from the closing quotes, which happen below - docstring = prefix + quote + docstring - if ( - len(indent) + len(prefix) + 2 * len(quote) + last_line_length - <= self.mode.line_length - ): - docstring += quote + if Preview.long_docstring_quotes_on_newline in self.mode: + leaf.value = prefix + quote + docstring + quote else: - docstring += "\n" + indent + quote + # Put closing quotes on new line if max line length exceeded + last_line_length = len(docstring.splitlines()[-1]) if docstring else 0 + + # Make the docstring apart from the closing quotes, which happen below + docstring = prefix + quote + docstring + if ( + len(indent) + len(prefix) + 2 * len(quote) + last_line_length + <= self.mode.line_length + ): + docstring += quote + else: + docstring += "\n" + indent + quote - leaf.value = docstring + leaf.value = docstring yield from self.visit_default(leaf) From ff75a6f3802f401b3a24ec93ccd206d242c6c0ec Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 21:28:20 +0100 Subject: [PATCH 08/18] using preview correctly this time --- src/black/linegen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index b07e8c34ddd..7af18cb914b 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -331,8 +331,6 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: quote = quote_char * quote_len if Preview.long_docstring_quotes_on_newline in self.mode: - leaf.value = prefix + quote + docstring + quote - else: # Put closing quotes on new line if max line length exceeded last_line_length = len(docstring.splitlines()[-1]) if docstring else 0 @@ -347,6 +345,8 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: docstring += "\n" + indent + quote leaf.value = docstring + else: + leaf.value = prefix + quote + docstring + quote yield from self.visit_default(leaf) From af631efbf9d4c3a2624762fb57ddd0688ab8537b Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 21:28:44 +0100 Subject: [PATCH 09/18] Changing test to fit with using the preview now --- tests/data/docstring.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/data/docstring.py b/tests/data/docstring.py index 91d292552ac..4fca6fc714b 100644 --- a/tests/data/docstring.py +++ b/tests/data/docstring.py @@ -189,8 +189,7 @@ def my_god_its_full_of_stars_2(): def docstring_almost_at_line_limit(): - """long docstring................................................................. - """ + """long docstring.................................................................""" def docstring_almost_at_line_limit2(): @@ -394,8 +393,7 @@ def my_god_its_full_of_stars_2(): def docstring_almost_at_line_limit(): - """long docstring................................................................. - """ + """long docstring.................................................................""" def docstring_almost_at_line_limit2(): From 06f907a085748890543d3c39eea03d6cfaa37f75 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 21:48:48 +0100 Subject: [PATCH 10/18] Adding preview tests and removing from regular test script --- tests/data/docstring.py | 30 ------------------------------ tests/data/docstring2.py | 33 +++++++++++++++++++++++++++++++++ tests/test_format.py | 1 + 3 files changed, 34 insertions(+), 30 deletions(-) create mode 100644 tests/data/docstring2.py diff --git a/tests/data/docstring.py b/tests/data/docstring.py index 4fca6fc714b..96bcf525b16 100644 --- a/tests/data/docstring.py +++ b/tests/data/docstring.py @@ -188,21 +188,6 @@ def my_god_its_full_of_stars_2(): "I'm sorry Dave " -def docstring_almost_at_line_limit(): - """long docstring.................................................................""" - - -def docstring_almost_at_line_limit2(): - """long docstring................................................................. - - .................................................................................. - """ - - -def docstring_at_line_limit(): - """long docstring................................................................""" - - # output class MyClass: @@ -390,18 +375,3 @@ def my_god_its_full_of_stars_1(): # the space below is actually a \u2001, removed in output def my_god_its_full_of_stars_2(): "I'm sorry Dave" - - -def docstring_almost_at_line_limit(): - """long docstring.................................................................""" - - -def docstring_almost_at_line_limit2(): - """long docstring................................................................. - - .................................................................................. - """ - - -def docstring_at_line_limit(): - """long docstring................................................................""" diff --git a/tests/data/docstring2.py b/tests/data/docstring2.py new file mode 100644 index 00000000000..86039c0fbf3 --- /dev/null +++ b/tests/data/docstring2.py @@ -0,0 +1,33 @@ +def docstring_almost_at_line_limit(): + """long docstring................................................................. + """ + + +def docstring_almost_at_line_limit2(): + """long docstring................................................................. + + .................................................................................. + """ + + +def docstring_at_line_limit(): + """long docstring................................................................""" + + +# output + + +def docstring_almost_at_line_limit(): + """long docstring................................................................. + """ + + +def docstring_almost_at_line_limit2(): + """long docstring................................................................. + + .................................................................................. + """ + + +def docstring_at_line_limit(): + """long docstring................................................................""" diff --git a/tests/test_format.py b/tests/test_format.py index 1916146e84d..5ce3098a888 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -91,6 +91,7 @@ "one_element_subscript", "remove_await_parens", "return_annotation_brackets", + "docstring2", ] SOURCES: List[str] = [ From ef3e6c367c43708807400ea8715d338a5c72fe0b Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 21:57:02 +0100 Subject: [PATCH 11/18] Putting tests back --- tests/data/docstring.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/data/docstring.py b/tests/data/docstring.py index 96bcf525b16..4fca6fc714b 100644 --- a/tests/data/docstring.py +++ b/tests/data/docstring.py @@ -188,6 +188,21 @@ def my_god_its_full_of_stars_2(): "I'm sorry Dave " +def docstring_almost_at_line_limit(): + """long docstring.................................................................""" + + +def docstring_almost_at_line_limit2(): + """long docstring................................................................. + + .................................................................................. + """ + + +def docstring_at_line_limit(): + """long docstring................................................................""" + + # output class MyClass: @@ -375,3 +390,18 @@ def my_god_its_full_of_stars_1(): # the space below is actually a \u2001, removed in output def my_god_its_full_of_stars_2(): "I'm sorry Dave" + + +def docstring_almost_at_line_limit(): + """long docstring.................................................................""" + + +def docstring_almost_at_line_limit2(): + """long docstring................................................................. + + .................................................................................. + """ + + +def docstring_at_line_limit(): + """long docstring................................................................""" From ed261cbfff769ee446fb926d4270c9c01857a67a Mon Sep 17 00:00:00 2001 From: Iain Dorrington Date: Wed, 4 May 2022 22:04:15 +0100 Subject: [PATCH 12/18] Update tests/test_format.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Felix Hildén --- tests/test_format.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_format.py b/tests/test_format.py index 5ce3098a888..2f08d1f273d 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -91,7 +91,7 @@ "one_element_subscript", "remove_await_parens", "return_annotation_brackets", - "docstring2", + "docstring_preview", ] SOURCES: List[str] = [ From 9923e9c6f4ab4c802e13bc66dd97bac4a5e30c16 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 22:23:44 +0100 Subject: [PATCH 13/18] The logic was slighly wrong. It needed to account for the fact that multiline strings have the indent included in all lines apart from the first one. Also, if the docstring has more than one line then we need to account for that there is only one set of quotes on the last line, not two --- src/black/linegen.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 7af18cb914b..a0493714376 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -332,17 +332,21 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: if Preview.long_docstring_quotes_on_newline in self.mode: # Put closing quotes on new line if max line length exceeded - last_line_length = len(docstring.splitlines()[-1]) if docstring else 0 + lines = docstring.splitlines() + last_line_length = len(lines[-1].lstrip()) if docstring else 0 + + # if docstring is one line, then we need two lots of quotes + len_quotes = 2 * quote_len if len(lines) == 1 else quote_len # Make the docstring apart from the closing quotes, which happen below docstring = prefix + quote + docstring if ( - len(indent) + len(prefix) + 2 * len(quote) + last_line_length - <= self.mode.line_length + len(indent) + len(prefix) + len_quotes + last_line_length + > self.mode.line_length ): - docstring += quote - else: docstring += "\n" + indent + quote + else: + docstring += quote leaf.value = docstring else: From 5ff6d8bc81f45d6b0a1105ac2b0c153c77053bc6 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 22:26:52 +0100 Subject: [PATCH 14/18] renaming preview test file --- tests/data/{docstring2.py => docstring_preview.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/data/{docstring2.py => docstring_preview.py} (100%) diff --git a/tests/data/docstring2.py b/tests/data/docstring_preview.py similarity index 100% rename from tests/data/docstring2.py rename to tests/data/docstring_preview.py From 70b4164ecb452658d0b8b7772b59b71c4437844d Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Wed, 4 May 2022 22:32:44 +0100 Subject: [PATCH 15/18] added new test that checks multiline docstrings at the line limit --- tests/data/docstring.py | 12 ++++++++++++ tests/data/docstring_preview.py | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/tests/data/docstring.py b/tests/data/docstring.py index 4fca6fc714b..7153be468c1 100644 --- a/tests/data/docstring.py +++ b/tests/data/docstring.py @@ -203,6 +203,12 @@ def docstring_at_line_limit(): """long docstring................................................................""" +def multiline_docstring_at_line_limit(): + """first line----------------------------------------------------------------------- + + second line----------------------------------------------------------------------""" + + # output class MyClass: @@ -405,3 +411,9 @@ def docstring_almost_at_line_limit2(): def docstring_at_line_limit(): """long docstring................................................................""" + + +def multiline_docstring_at_line_limit(): + """first line----------------------------------------------------------------------- + + second line----------------------------------------------------------------------""" diff --git a/tests/data/docstring_preview.py b/tests/data/docstring_preview.py index 86039c0fbf3..536ec108e2a 100644 --- a/tests/data/docstring_preview.py +++ b/tests/data/docstring_preview.py @@ -14,6 +14,12 @@ def docstring_at_line_limit(): """long docstring................................................................""" +def multiline_docstring_at_line_limit(): + """first line----------------------------------------------------------------------- + + second line----------------------------------------------------------------------""" + + # output @@ -31,3 +37,9 @@ def docstring_almost_at_line_limit2(): def docstring_at_line_limit(): """long docstring................................................................""" + + +def multiline_docstring_at_line_limit(): + """first line----------------------------------------------------------------------- + + second line----------------------------------------------------------------------""" From 9e8dc5f333548e39006e9356a28ade2d94f86626 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Thu, 5 May 2022 21:36:34 +0100 Subject: [PATCH 16/18] Fixing bug where prefix was sometimes considered in line length when it shouldn't be --- src/black/linegen.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index a0493714376..608886d033b 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -333,22 +333,21 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: if Preview.long_docstring_quotes_on_newline in self.mode: # Put closing quotes on new line if max line length exceeded lines = docstring.splitlines() - last_line_length = len(lines[-1].lstrip()) if docstring else 0 - - # if docstring is one line, then we need two lots of quotes - len_quotes = 2 * quote_len if len(lines) == 1 else quote_len - - # Make the docstring apart from the closing quotes, which happen below - docstring = prefix + quote + docstring - if ( - len(indent) + len(prefix) + len_quotes + last_line_length - > self.mode.line_length - ): - docstring += "\n" + indent + quote + last_line_length = len(lines[-1]) if docstring else 0 + + # if docstring is one line, then we need to add the length + # of the starting quotes and the prefix. Ending quote are + # handled later + if len(lines) == 1: + last_line_length += len(indent) + len(prefix) + quote_len + + # If adding closing quotes would cause the last line to exceed + # the maximum line length then put a line break before the + # closing quotes + if last_line_length + quote_len > self.mode.line_length: + leaf.value = prefix + quote + docstring + "\n" + indent + quote else: - docstring += quote - - leaf.value = docstring + leaf.value = prefix + quote + docstring + quote else: leaf.value = prefix + quote + docstring + quote From cf6700f7e2ae8234014357000d434bc4b8209e53 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Thu, 5 May 2022 21:45:34 +0100 Subject: [PATCH 17/18] Added tests with prefixes --- tests/data/docstring_preview.py | 48 +++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/tests/data/docstring_preview.py b/tests/data/docstring_preview.py index 536ec108e2a..2da4cd1acdb 100644 --- a/tests/data/docstring_preview.py +++ b/tests/data/docstring_preview.py @@ -3,23 +3,45 @@ def docstring_almost_at_line_limit(): """ -def docstring_almost_at_line_limit2(): +def docstring_almost_at_line_limit_with_prefix(): + f"""long docstring................................................................ + """ + + +def mulitline_docstring_almost_at_line_limit(): """long docstring................................................................. .................................................................................. """ +def mulitline_docstring_almost_at_line_limit_with_prefix(): + f"""long docstring................................................................ + + .................................................................................. + """ + + def docstring_at_line_limit(): """long docstring................................................................""" +def docstring_at_line_limit_with_prefix(): + f"""long docstring...............................................................""" + + def multiline_docstring_at_line_limit(): """first line----------------------------------------------------------------------- second line----------------------------------------------------------------------""" +def multiline_docstring_at_line_limit_with_prefix(): + f"""first line---------------------------------------------------------------------- + + second line----------------------------------------------------------------------""" + + # output @@ -28,18 +50,40 @@ def docstring_almost_at_line_limit(): """ -def docstring_almost_at_line_limit2(): +def docstring_almost_at_line_limit_with_prefix(): + f"""long docstring................................................................ + """ + + +def mulitline_docstring_almost_at_line_limit(): """long docstring................................................................. .................................................................................. """ +def mulitline_docstring_almost_at_line_limit_with_prefix(): + f"""long docstring................................................................ + + .................................................................................. + """ + + def docstring_at_line_limit(): """long docstring................................................................""" +def docstring_at_line_limit_with_prefix(): + f"""long docstring...............................................................""" + + def multiline_docstring_at_line_limit(): """first line----------------------------------------------------------------------- second line----------------------------------------------------------------------""" + + +def multiline_docstring_at_line_limit_with_prefix(): + f"""first line---------------------------------------------------------------------- + + second line----------------------------------------------------------------------""" From 85d5bf9241672c99ff6b37d800061084b4951934 Mon Sep 17 00:00:00 2001 From: idorrington92 Date: Thu, 5 May 2022 21:50:34 +0100 Subject: [PATCH 18/18] Fixing comments --- src/black/linegen.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/black/linegen.py b/src/black/linegen.py index 608886d033b..ff54e05c4e6 100644 --- a/src/black/linegen.py +++ b/src/black/linegen.py @@ -331,13 +331,15 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]: quote = quote_char * quote_len if Preview.long_docstring_quotes_on_newline in self.mode: - # Put closing quotes on new line if max line length exceeded + # We need to find the length of the last line of the docstring + # to find if we can add the closing quotes to the line without + # exceeding the maximum line length. + # If docstring is one line, then we need to add the length + # of the indent, prefix, and starting quotes. Ending quote are + # handled later lines = docstring.splitlines() last_line_length = len(lines[-1]) if docstring else 0 - # if docstring is one line, then we need to add the length - # of the starting quotes and the prefix. Ending quote are - # handled later if len(lines) == 1: last_line_length += len(indent) + len(prefix) + quote_len