Skip to content
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

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

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
1 change: 0 additions & 1 deletion yapf/pytree/comment_splicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def SpliceComments(tree):
# This is a list because Python 2.x doesn't have 'nonlocal' :)
prev_leaf = [None]
_AnnotateIndents(tree)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please retain this newline.

def _VisitNodeRec(node):
"""Recursively visit each node to splice comments into the AST."""
# This loop may insert into node.children, so we'll iterate over a copy.
Expand Down
15 changes: 9 additions & 6 deletions yapf/yapflib/format_decision_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,16 +934,19 @@ 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(list, dictionary,
# function call)logical line that is in many output lines
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify this comment, please? I'm not sure what it's trying to suggest.

Copy link
Author

@lizawang lizawang Aug 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the replies. I will fix all the problems today. We know that a dictionary, a list and an argument list is one logical line no matter how long it is with its entries all output in separate lines. Here it means that only when the comment is not inside this kind of logical line, aka, the comment has parent level equal to 0, we set the spaces_required_before to 0. Because we don't want the comments inside a dictionary to align with its parent (the dictionary variable name). Examples are given in pull request README.

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
47 changes: 38 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 @@ -277,7 +278,8 @@ def _AlignTrailingComments(final_lines):
for tok in line.tokens:
if (tok.is_comment and isinstance(tok.spaces_required_before, list) and
tok.value.startswith('#')):
# All trailing comments and comments that appear on a line by themselves
# All trailing comments
# NOTE not including comments that appear on a line by themselves
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reflow this comment. And place any "NOTE" comments at the end of the comment block.

# in this block should be indented at the same level. The block is
# terminated by an empty line or EOF. Enumerate through each line in
# the block and calculate the max line length. Once complete, use the
Expand Down Expand Up @@ -309,7 +311,16 @@ def _AlignTrailingComments(final_lines):
line_content = ''
pc_line_lengths = []

#NOTE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty note.

contain_object = False
for line_tok in this_line.tokens:

#NOTE if a line with inline comment is itself
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need a "NOTE" tag here (or in some other places).

# 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,31 @@ 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
'''
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a comment instead of multiline string.

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
62 changes: 61 additions & 1 deletion yapftests/yapf_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

from lib2to3.pgen2 import tokenize

from yapf.yapflib import errors
from yapf.yapflib import errors, reformatter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need this.

from yapf.yapflib import py3compat
from yapf.yapflib import style
from yapf.yapflib import yapf_api
Expand Down Expand Up @@ -1875,6 +1875,66 @@ def testDisabledLine(self):
""")
self._Check(unformatted_code, expected_formatted_code)

#NOTE test if don't align newline comments with inline comments
def testNewlineCommentsInsideInlineComment(self):
try:
style.SetGlobalStyle(
style.CreateStyleFromConfig('{align_newline_comments_with_inline_comments:false,'
'spaces_before_comment:15, 25,35}'))
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
""")
llines = yapf_test_helper.ParseAndUnwrap(unformatted_code)
self.assertCodeEqual(expected_formatted_code, reformatter.Reformat(llines))
finally:
style.SetGlobalStyle(self._OwnStyle())

# 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