diff --git a/src/docformatter/format.py b/src/docformatter/format.py index 5005a97..b2ff2d1 100644 --- a/src/docformatter/format.py +++ b/src/docformatter/format.py @@ -740,17 +740,21 @@ def _do_add_formatted_docstring( ): self.new_tokens[-2] = self.new_tokens[-2]._replace(line=_line) - # If a comment follows the docstring, skip adding a newline token for - # the line. - if not next_token.string.startswith("#"): - _new_tok = tokenize.TokenInfo( - type=tokenize.NEWLINE, - string="\n", - start=token.end, - end=(token.end[0], token.end[1] + 1), - line=_line, - ) - self.new_tokens.append(_new_tok) + # If a comment follows the docstring, the comment and its own NEWLINE + # token still have to be emitted, so skip adding a newline token and + # any blank lines here; doing so would place them before the comment + # and produce tokens whose positions move backwards. + if next_token.string.startswith("#"): + return + + _new_tok = tokenize.TokenInfo( + type=tokenize.NEWLINE, + string="\n", + start=token.end, + end=(token.end[0], token.end[1] + 1), + line=_line, + ) + self.new_tokens.append(_new_tok) # Add the appropriate number of NEWLINE tokens based on the type of # docstring. @@ -1123,7 +1127,8 @@ def _do_rewrite_docstring_blocks( if ( ( - self.new_tokens[-2].string == tokens[_idx + 1].string + len(self.new_tokens) > 1 + and self.new_tokens[-2].string == tokens[_idx + 1].string and _docstring_token.line == tokens[_idx + 1].line ) or tokens[_idx + 1].string == "\n" diff --git a/tests/formatter/test_do_format_code.py b/tests/formatter/test_do_format_code.py index 405398b..2d4e479 100644 --- a/tests/formatter/test_do_format_code.py +++ b/tests/formatter/test_do_format_code.py @@ -157,3 +157,24 @@ def test_do_format_code(test_key, test_args, args): result = uut._do_format_code(source) assert result == expected, f"\nFailed {test_key}\nExpected {expected}\nGot {result}" + + +@pytest.mark.integration +@pytest.mark.order(7) +@pytest.mark.parametrize("args", [NO_ARGS]) +def test_do_format_code_inline_comment_after_docstring(test_args, args): + """Docstring followed by an inline comment round-trips unchanged. + + See issue #347. The source is inlined here rather than added to + do_format_code.toml because the trailing comment is significant. + """ + uut = Formatter( + test_args, + sys.stderr, + sys.stdin, + sys.stdout, + ) + + source = '"""This is a comment.""" # noqa: D415\n' + + assert uut._do_format_code(source) == source