Skip to content

Add a new knob and a change for inline comment alignment #1022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
10 changes: 10 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
14 changes: 14 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
16 changes: 10 additions & 6 deletions yapf/yapflib/format_decision_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
45 changes: 36 additions & 9 deletions yapf/yapflib/reformatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from __future__ import unicode_literals

import collections
from distutils.errors import LinkError
import heapq
import re

Expand Down Expand Up @@ -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

Expand All @@ -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')
Expand All @@ -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:
Expand All @@ -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

Expand Down Expand Up @@ -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.
Expand Down
5 changes: 5 additions & 0 deletions yapf/yapflib/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -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("""\
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
59 changes: 59 additions & 0 deletions yapftests/yapf_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down