diff --git a/CHANGELOG b/CHANGELOG index 5ac3e6329..86013442e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,16 @@ # All notable changes to this project will be documented in this file. # This project adheres to [Semantic Versioning](http://semver.org/). +## [0.41.0] 2022-08-30 +### Added +- Add a new knob align_newline_comments_with_inline_comments. This can + be used when you don't want to align newline comments with inline comments. +### Changes +- Make the comments to start new alignment after multiline objects like + dictionaries, lists or function calls or function definitions. +- changes made to make the newline comments inside the multiline objects + not to indent as its parent level. + ## [0.40.0] UNRELEASED ### Added - Add a new Python parser to generate logical lines. diff --git a/README.rst b/README.rst index 5734a5d76..1392c65d6 100644 --- a/README.rst +++ b/README.rst @@ -390,6 +390,20 @@ Options:: Knobs ===== +``ALIGN_NEWLINE_COMMENTS_WITH_INLINE_COMMENTS`` + Align newline comments with the inline comments. The default setting is True. + If it is set to be False, new lines comments will not align with inline comments, + as following: + + .. code-block:: python + + def f(): + result = { + "a": 1, # comment inline + # comment newline + "abc": 2 + } + ``ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT`` Align closing bracket with visual indentation. diff --git a/yapf/yapflib/format_decision_state.py b/yapf/yapflib/format_decision_state.py index c299d1c85..b2c961384 100644 --- a/yapf/yapflib/format_decision_state.py +++ b/yapf/yapflib/format_decision_state.py @@ -934,16 +934,20 @@ def _GetNewlineColumn(self): previous = current.previous_token top_of_stack = self.stack[-1] + cont_aligned_indent = self._IndentWithContinuationAlignStyle( + top_of_stack.indent) + if isinstance(current.spaces_required_before, list): - # Don't set the value here, as we need to look at the lines near - # this one to determine the actual horizontal alignment value. - return 0 + # only when the commet is not inside an object logical line that has + # its entries output on separate output lines(e.g. list, dictionary, + # function call), aka when the comment' parent level is 0 + if self.paren_level == 0: + # Don't set the value here, as we need to look at the lines near + # this one to determine the actual horizontal alignment value. + return 0 elif current.spaces_required_before > 2 or self.line.disable: return current.spaces_required_before - cont_aligned_indent = self._IndentWithContinuationAlignStyle( - top_of_stack.indent) - if current.OpensScope(): return cont_aligned_indent if self.paren_level else self.first_indent diff --git a/yapf/yapflib/reformatter.py b/yapf/yapflib/reformatter.py index 14e0bde70..7adcd2a41 100644 --- a/yapf/yapflib/reformatter.py +++ b/yapf/yapflib/reformatter.py @@ -22,6 +22,7 @@ from __future__ import unicode_literals import collections +from distutils.errors import LinkError import heapq import re @@ -283,6 +284,8 @@ def _AlignTrailingComments(final_lines): # the block and calculate the max line length. Once complete, use the # first col value greater than that value and create the necessary for # each line accordingly. + # NOTE comments that appear on a line by themselves will be excluded if + # align_newline_comments_with_inline_comments is false. all_pc_line_lengths = [] # All pre-comment line lengths max_line_length = 0 @@ -309,7 +312,15 @@ def _AlignTrailingComments(final_lines): line_content = '' pc_line_lengths = [] + contain_object = False for line_tok in this_line.tokens: + + #if a line with inline comment is itself + # with newlines object, we want to start new alignment + if (line_tok.value in [')', ']','}'] + and line_tok.formatted_whitespace_prefix.startswith('\n')): + contain_object = True + whitespace_prefix = line_tok.formatted_whitespace_prefix newline_index = whitespace_prefix.rfind('\n') @@ -319,6 +330,7 @@ def _AlignTrailingComments(final_lines): whitespace_prefix = whitespace_prefix[newline_index + 1:] + # if comment starts with '\n', it will save length 0 if line_tok.is_comment: pc_line_lengths.append(len(line_content)) else: @@ -329,6 +341,11 @@ def _AlignTrailingComments(final_lines): all_pc_line_lengths.append(pc_line_lengths) + #NOTE if it's a logical line with object(dict/list/tuple) + # that have its items in separate lines + if contain_object: + break + # Calculate the aligned column value max_line_length += 2 @@ -359,19 +376,29 @@ def _AlignTrailingComments(final_lines): # we need to apply a whitespace prefix to each line. whitespace = ' ' * ( aligned_col - pc_line_lengths[pc_line_length_index] - 1) - pc_line_length_index += 1 - - line_content = [] - for comment_line_index, comment_line in enumerate( - line_tok.value.split('\n')): - line_content.append('{}{}'.format(whitespace, + #this is added when we don't want comments on newlines + #to align with comments inline + if not style.Get('ALIGN_NEWLINE_COMMENTS_WITH_INLINE_COMMENTS'): + # if this comment starts with '\n', pass and go to next comment + if pc_line_lengths[pc_line_length_index] == 0: + pc_line_length_index += 1 + continue + line_content = '{}{}'.format(whitespace, line_tok.value.strip()) + else: + line_content = [] + for comment_line_index, comment_line in enumerate( + line_tok.value.split('\n')): + line_content.append('{}{}'.format(whitespace, comment_line.strip())) - if comment_line_index == 0: - whitespace = ' ' * (aligned_col - 1) + if comment_line_index == 0: + whitespace = ' ' * (aligned_col - 1) - line_content = '\n'.join(line_content) + line_content = '\n'.join(line_content) + + # after process, go to next pre comment tokens length + pc_line_length_index += 1 # Account for initial whitespace already slated for the # beginning of the line. diff --git a/yapf/yapflib/style.py b/yapf/yapflib/style.py index 233a64e6b..e861ed101 100644 --- a/yapf/yapflib/style.py +++ b/yapf/yapflib/style.py @@ -54,6 +54,9 @@ def SetGlobalStyle(style): _STYLE_HELP = dict( ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=textwrap.dedent("""\ Align closing bracket with visual indentation."""), + ALIGN_NEWLINE_COMMENTS_WITH_INLINE_COMMENTS=textwrap.dedent("""\ + Align comments on newlines with the inline comments in the + same block. This is the default setting for yapf."""), ALLOW_MULTILINE_LAMBDAS=textwrap.dedent("""\ Allow lambdas to be formatted on more than one line."""), ALLOW_MULTILINE_DICTIONARY_KEYS=textwrap.dedent("""\ @@ -419,6 +422,7 @@ def CreatePEP8Style(): """Create the PEP8 formatting style.""" return dict( ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=True, + ALIGN_NEWLINE_COMMENTS_WITH_INLINE_COMMENTS=True, ALLOW_MULTILINE_LAMBDAS=False, ALLOW_MULTILINE_DICTIONARY_KEYS=False, ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS=True, @@ -607,6 +611,7 @@ def _IntOrIntListConverter(s): # Note: this dict has to map all the supported style options. _STYLE_OPTION_VALUE_CONVERTER = dict( ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=_BoolConverter, + ALIGN_NEWLINE_COMMENTS_WITH_INLINE_COMMENTS=_BoolConverter, ALLOW_MULTILINE_LAMBDAS=_BoolConverter, ALLOW_MULTILINE_DICTIONARY_KEYS=_BoolConverter, ALLOW_SPLIT_BEFORE_DEFAULT_OR_NAMED_ASSIGNS=_BoolConverter, diff --git a/yapftests/yapf_test.py b/yapftests/yapf_test.py index 2330f4e18..60aa5e99a 100644 --- a/yapftests/yapf_test.py +++ b/yapftests/yapf_test.py @@ -1875,6 +1875,65 @@ def testDisabledLine(self): """) self._Check(unformatted_code, expected_formatted_code) + # test if don't align newline comments with inline comments + def testNewlineCommentsInsideInlineComment(self): + unformatted_code = textwrap.dedent("""\ + if True: + if True: + if True: + func(1) # comment 1 + func(2) # comment 2 + # comment 3 + func(3) # comment 4 inline + # comment 4 newline + # comment 4 newline + + # comment 5 Not aligned + """) # noqa + expected_formatted_code = textwrap.dedent("""\ + if True: + if True: + if True: + func(1) # comment 1 + func(2) # comment 2 + # comment 3 + func(3) # comment 4 inline + # comment 4 newline + # comment 4 newline + + # comment 5 Not aligned + """) + + formatted_code, _ = yapf_api.FormatCode( + unformatted_code, style_config=style.SetGlobalStyle(style.CreateStyleFromConfig( + '{align_newline_comments_with_inline_comments:false,' + 'spaces_before_comment:15, 25,35}'))) + self.assertCodeEqual(expected_formatted_code, formatted_code) + + + # test when there is an object with newline entries in between + def testObjectWithNewlineEntriesInBetween(self): + + unformatted_code = textwrap.dedent("""\ + func( 1 ) # Line 1 + func( 2 ) # Line 2 + d = {key1: value1, key2: value2, key3: value3,} # Line 3 + func( 3 ) # Line 4 + func( 4 ) # line 5 + """) # noqa + expected_formatted_code = textwrap.dedent("""\ + func(1) # Line 1 + func(2) # Line 2 + d = { + key1: value1, + key2: value2, + key3: value3, + } # Line 3 + func(3) # Line 4 + func(4) # line 5 + """) + self._Check(unformatted_code, expected_formatted_code) + class _SpacesAroundDictListTupleTestImpl(unittest.TestCase):