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

Feature align dict colon #1032

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

lizawang
Copy link

@lizawang lizawang commented Oct 3, 2022

Hello all yapf founders!

I have some new features I want to share with the yapf community.
This is also a response to issue #194 and issue #346.

The alignment features added are referred to the yapf function _AlignTrailingComments(final_lines).

The knobs

The main functions for all the knobs are added in reformatter.py.

knob: ALIGN_DICT_COLON

This is an option to align colons inside dictionary.

  • The condition : it is only effective if all key-value pairs inside the dictionary are placed on separate lines, e.g. when 'EACH_DICT_ENTRY_ON_SEPARATE_LINE' is set True.
  • New block of key-value pairs starts with new alignment. There are 3 cases featured as an indication for new alignment block:
    • Each nested dictionary is a new block:
      fields = [
          {
              "field"    : "ediid",
              "type"     : "text",
              "required" : True,
              "html"     :
                  {
                      "attr"   : 'style="width: 250px;" maxlength="30"',
                      "page"   : 0,
                      "span"   : 8,
                      "column" : 0,
                  },
          }
      ]
      
    • A comment line inside a dictionary of same level creates a new block:
      fields =  
          {
              "field" : "ediid",
              "type"  : "text",
              # key: value
              "required" : True,
          }
      
    • An object(a list/a dictionary/a set/a argument list) with its items in newlines in between creates a new block inside the dictionary of the same level:
      fields = {
          "field"      : "ediid",
          "long_value" :
              get_dataframe_from_excel_sheet(
                  filepath, usecols = [ 0 ], header = None, dtype = str ),
          "type"     : "text",
          "required" : True,
      }
      

knob: NEW_ALIGNMENT_AFTER_COMMENTLINE

This option is made under the consideration that some programmers may prefer to remain the alignment with comment lines inside.

  • If it is set False, a comment line in between won't create a new alignment block. Entries before and after the comment line/lines will align with their colons.

Preparations

To make the alignment functions possible, some other changes are made to other files.

  • format_token.py: necessary boolean functions are added, is_dict_key, is_dict_key_start, is_dict_value.

Finally

In the end, the knobs are added into the style.py and tests for each one are also updated in yapftests folder.

  • style.py: all the 2 knobs are set to be False by default.
  • Unitests are added into reformatter_basic_test.py.



@JackLilhammers
Copy link

Bump!

@gsmethells
Copy link

+1

@Spitfire1900
Copy link
Contributor

@lizawang this PR is blocked by merge conflicts.

@Spitfire1900
Copy link
Contributor

I don't see anything standing out besides resolving merge conflicts. @bwendling. @EeyoreLee , @char101 any thoughts ?

@EeyoreLee
Copy link
Contributor

@Spitfire1900 - Thanks for your invitation. I think this feature should be considered cause the feature-request has 42 upvotes. A code review is essential before merging, e.g., the function _AlignDictColon has too many lines, a space missing between # and TODO. In addition, the name of second knob should be considered a more readable one as I can't understand the function of this knob clearly enough through the name or explanation unless I walk through the whole PR overview.

.. code-block:: python

fields =
{
Copy link
Member

Choose a reason for hiding this comment

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

This isn't legal Python. The opening bracket should go on the previous line.

}

``NEW_ALIGNMENT_AFTER_COMMENTLINE``
Make it optional to start a new alignmetn block for assignment
Copy link
Member

Choose a reason for hiding this comment

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

s/alignmetn/alignment/

@@ -89,7 +89,7 @@ def Visit_classdef(self, node): # pylint: disable=invalid-name
if len(node.children) > 4:
# opening '('
_SetUnbreakable(node.children[2])
# ':'
# ':'
Copy link
Member

Choose a reason for hiding this comment

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

Probably don't need this change in the commit.

@@ -54,6 +54,14 @@ def SetGlobalStyle(style):
_STYLE_HELP = dict(
ALIGN_CLOSING_BRACKET_WITH_VISUAL_INDENT=textwrap.dedent("""\
Align closing bracket with visual indentation."""),
ALIGN_DICT_COLON=textwrap.dedent("""\
Copy link
Member

Choose a reason for hiding this comment

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

The other two knobs aren't added here?

@@ -394,6 +399,186 @@ def _AlignTrailingComments(final_lines):
final_lines_index += 1


def _AlignDictColon(final_lines):
"""Align colons in a dict to the same column"""
"""NOTE One (nested) dict/list is one logical line!"""
Copy link
Member

Choose a reason for hiding this comment

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

Add this line as part of the previous docstring.

prefix = line_tok.formatted_whitespace_prefix
newline = prefix.startswith('\n')
if newline:
# if comments inbetween, save, reset and continue to caluclate new alignment
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 comment to 80-columns.

this_line = line

line_tokens = this_line.tokens
for open_index in range(len(line_tokens)):
Copy link
Member

Choose a reason for hiding this comment

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

Use enumerate?

line_tok = line_tokens[open_index]

# check each time if the detected dict is the dict we aim for
if line_tok.value == '{' and line_tok.next_token.formatted_whitespace_prefix.startswith(
Copy link
Member

Choose a reason for hiding this comment

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

Please put the conditional in parentheses and reformat to 80-columns.

@@ -394,6 +399,186 @@ def _AlignTrailingComments(final_lines):
final_lines_index += 1


def _AlignDictColon(final_lines):
Copy link
Member

Choose a reason for hiding this comment

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

I know that this is similar to the _AlignTrailingComments function, but it's also much longer. Could you split some of the code out into separate functions to make it more readable?

@Spitfire1900
Copy link
Contributor

Thanks for reviewing @bwendling 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants