Skip to content

Commit 3d4e701

Browse files
authored
Merge pull request #50 from MarketSquare/feature/align_variables_section
Align variables section transformer
2 parents 41e7f1f + 8754fb4 commit 3d4e701

File tree

7 files changed

+148
-0
lines changed

7 files changed

+148
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from collections import defaultdict
2+
3+
from robot.api.parsing import (
4+
ModelTransformer,
5+
Token
6+
)
7+
from robot.parsing.model import Statement
8+
9+
from robotidy.decorators import check_start_end_line
10+
11+
12+
class AlignVariablesSection(ModelTransformer):
13+
"""
14+
Align variables in *** Variables *** section to columns.
15+
16+
Following code::
17+
18+
*** Variables ***
19+
${VAR} 1
20+
${LONGER_NAME} 2
21+
&{MULTILINE} a=b
22+
... b=c
23+
24+
Will be transformed to::
25+
26+
*** Variables ***
27+
${VAR} 1
28+
${LONGER_NAME} 2
29+
&{MULTILINE} a=b
30+
... b=c
31+
32+
Supports global formatting params: ``--startline`` and ``--endline``.
33+
"""
34+
@check_start_end_line
35+
def visit_VariableSection(self, node): # noqa
36+
statements = []
37+
for child in node.body:
38+
if child.type == 'EOL':
39+
statements.append(child)
40+
else:
41+
statements.append(list(self.tokens_by_lines(child)))
42+
nodes_to_be_aligned = [st for st in statements if isinstance(st, list)]
43+
if not nodes_to_be_aligned:
44+
return node
45+
look_up = self.create_look_up(nodes_to_be_aligned) # for every col find longest value
46+
node.body = self.align_rows(statements, look_up)
47+
return node
48+
49+
def align_rows(self, statements, look_up):
50+
aligned_statements = []
51+
for st in statements:
52+
if not isinstance(st, list):
53+
aligned_statements.append(st)
54+
continue
55+
aligned_statement = []
56+
for line in st:
57+
for index, token in enumerate(line[:-2]):
58+
aligned_statement.append(token)
59+
aligned_statement.append(Token(Token.SEPARATOR, (look_up[index] - len(token.value) + 4) * ' '))
60+
last_token = line[-2]
61+
# remove leading whitespace before token
62+
last_token.value = last_token.value.strip() if last_token.value else last_token.value
63+
aligned_statement.append(last_token)
64+
aligned_statement.append(line[-1]) # eol
65+
aligned_statements.append(Statement.from_tokens(aligned_statement))
66+
return aligned_statements
67+
68+
def tokens_by_lines(self, node):
69+
for index, line in enumerate(node.lines):
70+
if line:
71+
if line[0].type == Token.VARIABLE and not line[0].value:
72+
# if variable is prefixed with spaces
73+
line = line[1:]
74+
elif line[0].type == Token.ARGUMENT:
75+
line[0].value = line[0].value.strip() if line[0].value else line[0].value
76+
yield [token for token in line if token.type not in ('SEPARATOR', 'EOS')]
77+
78+
@staticmethod
79+
def create_look_up(statements):
80+
look_up = defaultdict(int)
81+
for st in statements:
82+
for line in st:
83+
for index, token in enumerate(line):
84+
look_up[index] = max(look_up[index], len(token.value))
85+
return {index: AlignVariablesSection.round_to_four(length) for index, length in look_up.items()}
86+
87+
@staticmethod
88+
def round_to_four(number):
89+
div = number % 4
90+
if div:
91+
return number + 4 - div
92+
return number

robotidy/transformers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from robot.utils.importer import Importer
99

1010
TRANSFORMERS = frozenset((
11+
'AlignVariablesSection',
1112
'AssignmentNormalizer',
1213
'DiscardEmptySections',
1314
'NormalizeNewLines',
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
*** Variables ***
2+
# some comment
3+
4+
${VARIABLE 1}= 10 # comment
5+
@{LIST} a b c d
6+
${LONGER_NAME_THAT_GOES_AND_GOES} = longer value that goes and goes
7+
8+
&{MULTILINE} a=b
9+
... b=c
10+
... d=1
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
*** Variables ***
2+
# some comment
3+
4+
${VARIABLE 1} 10 # comment
5+
@{LIST} a b c d
6+
${LONGER_NAME_THAT_GOES_AND_GOES} longer value that goes and goes
7+
8+
&{MULTILINE} a=b
9+
... b=c
10+
... d=1
11+
${invalid}
12+
${invalid_more}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
*** Variables ***
2+
# some comment
3+
4+
${VARIABLE 1}= 10 # comment
5+
@{LIST} a b c d
6+
${LONGER_NAME_THAT_GOES_AND_GOES} = longer value that goes and goes
7+
8+
&{MULTILINE} a=b
9+
... b=c
10+
... d=1
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
*** Variables ***
2+
# some comment
3+
4+
${VARIABLE 1} 10 # comment
5+
@{LIST} a b c d
6+
${LONGER_NAME_THAT_GOES_AND_GOES} longer value that goes and goes
7+
8+
&{MULTILINE} a=b
9+
... b=c
10+
... d=1
11+
${invalid}
12+
${invalid_more}

tests/atest/transformers/test_transformers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,14 @@ def test_split_too_long_lines_split_on_every_arg(self):
285285
expected=['split_on_every_arg.robot'],
286286
config=':line_length=80:split_on_every_arg=True -s 4'
287287
)
288+
289+
290+
@patch('robotidy.app.Robotidy.save_model', new=save_tmp_model)
291+
class TestAlignVariablesSection:
292+
TRANSFORMER_NAME = 'AlignVariablesSection'
293+
294+
def test_align_variables(self):
295+
run_tidy_and_compare(self.TRANSFORMER_NAME, sources=['tests.robot'])
296+
297+
def test_align_with_optional_equal_signs(self):
298+
run_tidy_and_compare(self.TRANSFORMER_NAME, sources=['optional_equal_sign.robot'])

0 commit comments

Comments
 (0)