Skip to content

Commit 9c29fca

Browse files
committed
Merge branch 'multiline-lambdas', fixes #191
2 parents 236a9a8 + 89b8200 commit 9c29fca

32 files changed

+1611
-549
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22

33
## [master]
44

5+
### Added
6+
- Added support for multi-statement lambdas (#191)
7+
58
### Changed
69
- Removed `private-method-call` linter check due to false positives when calling `super._foo()`
710
- Fixed support for `get_node` syntax to accommodate for `$/(...)`
11+
- Changed formatting of some uni-statement lambdas
12+
- Changed formatting of multi-statement, inline lambdas
813

914
## [4.3.1] 2024-08-24
1015

gdtoolkit/formatter/expression.py

Lines changed: 66 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import Dict, Callable, List, Optional
2+
from importlib import import_module
23

34
from lark import Tree, Token
45
from lark.tree import Meta
@@ -149,6 +150,8 @@ def _format_foldable_to_multiple_lines(
149150
"dict": _format_dict_to_multiple_lines,
150151
"c_dict_element": _format_kv_pair_to_multiple_lines,
151152
"eq_dict_element": _format_kv_pair_to_multiple_lines,
153+
"lambda": _format_lambda_to_multiple_lines,
154+
"lambda_header": _format_lambda_header_to_multiple_lines,
152155
# fake expressions:
153156
"func_args": _format_args_to_multiple_lines,
154157
"func_arg_regular": _format_func_arg_to_multiple_lines,
@@ -165,33 +168,6 @@ def _format_foldable_to_multiple_lines(
165168
),
166169
"annotation": _format_annotation_to_multiple_lines,
167170
"annotation_args": _format_args_to_multiple_lines,
168-
"inline_lambda": _format_inline_lambda_to_multiple_lines,
169-
"lambda_header": _format_lambda_header_to_multiple_lines,
170-
"inline_lambda_statements": _format_inline_lambda_statements_to_multiple_lines,
171-
"pass_stmt": _format_concrete_expression_to_single_line,
172-
"return_stmt": lambda e, ec, c: _append_to_expression_context_and_pass_standalone(
173-
"return ", e.children[0], ec, c
174-
),
175-
"expr_stmt": lambda e, ec, c: _format_standalone_expression(
176-
e.children[0].children[0], ec, c
177-
),
178-
"func_var_stmt": lambda e, ec, c: _format_standalone_expression(
179-
e.children[0], ec, c
180-
),
181-
"func_var_empty": _format_concrete_expression_to_single_line,
182-
"func_var_assigned": lambda e, ec, c: _append_to_expression_context_and_pass_standalone(
183-
f"var {expression_to_str(e.children[0])} = ", e.children[1], ec, c
184-
),
185-
"func_var_typed": _format_concrete_expression_to_single_line,
186-
"func_var_typed_assgnd": lambda e, ec, c: _append_to_expression_context_and_pass_standalone(
187-
f"var {expression_to_str(e.children[0])}: {expression_to_str(e.children[1])} = ",
188-
e.children[2],
189-
ec,
190-
c,
191-
),
192-
"func_var_inf": lambda e, ec, c: _append_to_expression_context_and_pass_standalone(
193-
f"var {expression_to_str(e.children[0])} := ", e.children[1], ec, c
194-
),
195171
"dot_chain": _format_dot_chain_to_multiple_lines,
196172
"actual_getattr_call": _format_call_expression_to_multiple_lines,
197173
"actual_subscr_expr": _format_subscription_to_multiple_lines,
@@ -645,100 +621,6 @@ def _format_annotation_to_multiple_lines(
645621
)
646622

647623

648-
def _format_inline_lambda_to_multiple_lines(
649-
inline_lambda: Tree,
650-
expression_context: ExpressionContext,
651-
context: Context,
652-
) -> FormattedLines:
653-
expression_context_for_header = ExpressionContext(
654-
expression_context.prefix_string, expression_context.prefix_line, "", -1
655-
)
656-
header_lines = _format_concrete_expression(
657-
inline_lambda.children[0], expression_context_for_header, context
658-
)
659-
last_header_line_number, last_header_line = header_lines[-1]
660-
assert last_header_line_number is not None
661-
expression_context_for_statements = ExpressionContext(
662-
f"{last_header_line.strip()} ",
663-
last_header_line_number, # type:ignore
664-
expression_context.suffix_string,
665-
expression_context.suffix_line,
666-
)
667-
fake_meta = Meta()
668-
fake_meta.line = get_line(inline_lambda.children[1])
669-
fake_meta.end_line = get_end_line(inline_lambda.children[-1])
670-
fake_expression = Tree(
671-
"inline_lambda_statements", inline_lambda.children[1:], fake_meta
672-
)
673-
statement_lines = _format_concrete_expression(
674-
fake_expression, expression_context_for_statements, context
675-
)
676-
return header_lines[:-1] + statement_lines
677-
678-
679-
def _format_lambda_header_to_multiple_lines(
680-
lambda_header: Tree,
681-
expression_context: ExpressionContext,
682-
context: Context,
683-
) -> FormattedLines:
684-
append_to_prefix = (
685-
f"func {lambda_header.children[0].value}"
686-
if isinstance(lambda_header.children[0], Token)
687-
else "func"
688-
)
689-
args_offset = 1 if isinstance(lambda_header.children[0], Token) else 0
690-
theres_something_after_args = len(lambda_header.children) > args_offset + 1
691-
optional_type_hint = (
692-
f" -> {lambda_header.children[args_offset+1]}"
693-
if theres_something_after_args
694-
else ""
695-
)
696-
prepend_to_suffix = f"{optional_type_hint}:"
697-
new_expression_context = ExpressionContext(
698-
f"{expression_context.prefix_string}{append_to_prefix}",
699-
expression_context.prefix_line,
700-
f"{prepend_to_suffix}{expression_context.suffix_string}",
701-
expression_context.suffix_line,
702-
)
703-
return _format_concrete_expression(
704-
lambda_header.children[args_offset], new_expression_context, context
705-
)
706-
707-
708-
def _format_inline_lambda_statements_to_multiple_lines(
709-
inline_lambda_statements: Tree,
710-
expression_context: ExpressionContext,
711-
context: Context,
712-
) -> FormattedLines:
713-
lambda_statements = inline_lambda_statements.children
714-
if len(lambda_statements) == 1:
715-
return _format_concrete_expression(
716-
lambda_statements[0], expression_context, context
717-
)
718-
expression_context_for_first_statement = ExpressionContext(
719-
expression_context.prefix_string, expression_context.prefix_line, "", -1
720-
)
721-
first_statement_formatted_lines = _format_concrete_expression(
722-
lambda_statements[0], expression_context_for_first_statement, context
723-
)
724-
last_line_number, last_line = first_statement_formatted_lines[-1]
725-
assert last_line_number is not None
726-
remaining_statements_prefix = last_line.strip()
727-
remaining_statements_expression_context = ExpressionContext(
728-
f"{remaining_statements_prefix} ; ",
729-
last_line_number, # type: ignore
730-
expression_context.suffix_string,
731-
expression_context.suffix_line,
732-
)
733-
fake_meta = Meta()
734-
fake_meta.line = get_line(lambda_statements[1])
735-
fake_meta.end_line = get_end_line(lambda_statements[-1])
736-
fake_expression = Tree("inline_lambda_statements", lambda_statements[1:], fake_meta)
737-
return first_statement_formatted_lines[:-1] + _format_concrete_expression(
738-
fake_expression, remaining_statements_expression_context, context
739-
)
740-
741-
742624
def _collapse_getattr_tree_to_dot_chain(expression: Tree) -> Tree:
743625
reversed_dot_chain_children = [] # type: List[Node]
744626
pending_getattr_call_to_match = None
@@ -883,3 +765,66 @@ def _format_dot_chain_to_multiple_lines_bottom_up(
883765
fake_meta,
884766
)
885767
return _format_concrete_expression(new_actual_expr, expression_context, context)
768+
769+
770+
def _format_lambda_to_multiple_lines(
771+
a_lambda: Tree,
772+
expression_context: ExpressionContext,
773+
context: Context,
774+
) -> FormattedLines:
775+
expression_context_for_header = ExpressionContext(
776+
expression_context.prefix_string, expression_context.prefix_line, "", -1
777+
)
778+
header_lines = _format_concrete_expression(
779+
a_lambda.children[0], expression_context_for_header, context
780+
)
781+
782+
block_module = import_module("gdtoolkit.formatter.block")
783+
function_statement_module = import_module("gdtoolkit.formatter.function_statement")
784+
child_context = context.create_child_context(expression_context.prefix_line)
785+
(block_lines, _) = block_module.format_block(
786+
a_lambda.children[1:],
787+
function_statement_module.format_func_statement,
788+
child_context,
789+
)
790+
last_block_line_number, last_block_line_content = block_lines[-1]
791+
792+
return (
793+
header_lines
794+
+ block_lines[:-1]
795+
+ [
796+
(
797+
last_block_line_number,
798+
f"{last_block_line_content}{expression_context.suffix_string}",
799+
)
800+
]
801+
)
802+
803+
804+
def _format_lambda_header_to_multiple_lines(
805+
lambda_header: Tree,
806+
expression_context: ExpressionContext,
807+
context: Context,
808+
) -> FormattedLines:
809+
append_to_prefix = (
810+
f"func {lambda_header.children[0].value}"
811+
if isinstance(lambda_header.children[0], Token)
812+
else "func"
813+
)
814+
args_offset = 1 if isinstance(lambda_header.children[0], Token) else 0
815+
theres_something_after_args = len(lambda_header.children) > args_offset + 1
816+
optional_type_hint = (
817+
f" -> {lambda_header.children[args_offset+1]}"
818+
if theres_something_after_args
819+
else ""
820+
)
821+
prepend_to_suffix = f"{optional_type_hint}:"
822+
new_expression_context = ExpressionContext(
823+
f"{expression_context.prefix_string}{append_to_prefix}",
824+
expression_context.prefix_line,
825+
f"{prepend_to_suffix}{expression_context.suffix_string}",
826+
expression_context.suffix_line,
827+
)
828+
return _format_concrete_expression(
829+
lambda_header.children[args_offset], new_expression_context, context
830+
)

gdtoolkit/formatter/expression_to_str.py

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
remove_outer_parentheses,
1010
has_trailing_comma,
1111
)
12+
from .function_statement_to_str import function_statement_to_str
1213

1314

1415
def standalone_expression_to_str(expression: Node) -> str:
@@ -87,6 +88,8 @@ def expression_to_str(expression: Node) -> str:
8788
[expression_to_str(n) for n in e.children]
8889
),
8990
"string_name": lambda e: f"&{expression_to_str(e.children[0])}",
91+
"lambda": _lambda_to_str,
92+
"lambda_header": _lambda_header_to_str,
9093
# fake expressions:
9194
"func_args": _args_to_str,
9295
"func_arg_regular": lambda e: "{}{}".format(
@@ -121,30 +124,6 @@ def expression_to_str(expression: Node) -> str:
121124
"trailing_comma": lambda _: "",
122125
"annotation": _annotation_to_str,
123126
"annotation_args": _annotation_args_to_str,
124-
"inline_lambda": _inline_lambda_to_str,
125-
"lambda_header": _lambda_header_to_str,
126-
"inline_lambda_statements": lambda e: " ; ".join(
127-
expression_to_str(statement) for statement in e.children
128-
),
129-
"pass_stmt": lambda _: "pass",
130-
"return_stmt": lambda e: f"return {standalone_expression_to_str(e.children[0])}",
131-
"expr_stmt": lambda e: f"{standalone_expression_to_str(e.children[0])}",
132-
"func_var_stmt": lambda e: expression_to_str(e.children[0]),
133-
"func_var_empty": lambda e: f"var {e.children[0].value}",
134-
"func_var_assigned": lambda e: "var {} = {}".format(
135-
e.children[0].value, standalone_expression_to_str(e.children[1])
136-
),
137-
"func_var_inf": lambda e: "var {} := {}".format(
138-
e.children[0].value, standalone_expression_to_str(e.children[1])
139-
),
140-
"func_var_typed": lambda e: "var {}: {}".format(
141-
e.children[0].value, standalone_expression_to_str(e.children[1])
142-
),
143-
"func_var_typed_assgnd": lambda e: "var {}: {} = {}".format(
144-
e.children[0].value,
145-
e.children[1].value,
146-
standalone_expression_to_str(e.children[2]),
147-
),
148127
"non_foldable_dot_chain": lambda e: "".join(map(expression_to_str, e.children)),
149128
"actual_getattr_call": _getattr_call_to_str,
150129
"actual_subscr_expr": _subscription_to_str,
@@ -242,10 +221,11 @@ def _annotation_args_to_str(annotation: Tree) -> str:
242221
return "({}{})".format(", ".join(elements), trailing_comma)
243222

244223

245-
def _inline_lambda_to_str(inline_lambda: Tree) -> str:
246-
fake_expression = Tree("inline_lambda_statements", inline_lambda.children[1:])
224+
def _lambda_to_str(a_lambda: Tree) -> str:
225+
assert len(a_lambda.children) == 2
247226
return "{} {}".format(
248-
expression_to_str(inline_lambda.children[0]), expression_to_str(fake_expression)
227+
expression_to_str(a_lambda.children[0]),
228+
function_statement_to_str(a_lambda.children[1]),
249229
)
250230

251231

gdtoolkit/formatter/expression_utils.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ def is_trailing_comma(expression: Node) -> bool:
4747
def is_expression_forcing_multiple_lines(
4848
expression: Node, standalone_comments: List[Optional[str]]
4949
) -> bool:
50-
if has_trailing_comma(expression):
51-
return True
52-
if _is_multiline_string(expression):
50+
if has_trailing_comma(expression) or _is_multiline_string(expression):
5351
return True
5452
if isinstance(expression, Token):
5553
return False
56-
if _has_standalone_comments(expression, standalone_comments):
54+
if (
55+
_has_standalone_comments(expression, standalone_comments)
56+
or _is_multistatement_lambda(expression)
57+
or _is_unistatement_lambda_with_compoud_statement(expression)
58+
):
5759
return True
5860
for child in expression.children:
5961
if is_expression_forcing_multiple_lines(child, standalone_comments):
@@ -82,10 +84,30 @@ def _is_multiline_string(expression: Node) -> bool:
8284

8385
def _has_standalone_comments(
8486
expression: Tree, standalone_comments: List[Optional[str]]
85-
):
87+
) -> bool:
8688
return any(
8789
comment is not None
8890
for comment in standalone_comments[
8991
get_line(expression) : get_end_line(expression)
9092
]
9193
)
94+
95+
96+
def _is_multistatement_lambda(expression: Tree) -> bool:
97+
return (
98+
isinstance(expression, Tree)
99+
and expression.data == "lambda"
100+
and len(expression.children) > 2
101+
)
102+
103+
104+
# TODO: remove once such statements are supported
105+
def _is_unistatement_lambda_with_compoud_statement(expression: Tree) -> bool:
106+
return (
107+
isinstance(expression, Tree)
108+
and expression.data == "lambda"
109+
and len(expression.children) == 2
110+
and isinstance(expression.children[1], Tree)
111+
and expression.children[1].data
112+
in ["if_stmt", "while_stmt", "for_stmt", "for_stmt_typed", "match_stmt"]
113+
)

0 commit comments

Comments
 (0)