prettycsharp 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prettycsharp/__init__.py +9 -0
- prettycsharp/__main__.py +5 -0
- prettycsharp/cli.py +250 -0
- prettycsharp/config.py +263 -0
- prettycsharp/doc.py +537 -0
- prettycsharp/formatter.py +106 -0
- prettycsharp/ignore.py +184 -0
- prettycsharp/parsing.py +53 -0
- prettycsharp/printer/__init__.py +28 -0
- prettycsharp/printer/core.py +540 -0
- prettycsharp/printer/declarations.py +623 -0
- prettycsharp/printer/expressions.py +1107 -0
- prettycsharp/printer/statements.py +603 -0
- prettycsharp/project.py +214 -0
- prettycsharp/py.typed +0 -0
- prettycsharp/rewrites.py +732 -0
- prettycsharp/settings.py +192 -0
- prettycsharp/syntax.py +367 -0
- prettycsharp/verify.py +312 -0
- prettycsharp-1.0.0.dist-info/METADATA +212 -0
- prettycsharp-1.0.0.dist-info/RECORD +24 -0
- prettycsharp-1.0.0.dist-info/WHEEL +4 -0
- prettycsharp-1.0.0.dist-info/entry_points.txt +2 -0
- prettycsharp-1.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1107 @@
|
|
|
1
|
+
"""Expressions: operators, calls and member chains, initializers, lambdas, patterns."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
|
|
9
|
+
from prettycsharp.doc import (
|
|
10
|
+
HARDLINE,
|
|
11
|
+
LINE,
|
|
12
|
+
SOFTLINE,
|
|
13
|
+
Align,
|
|
14
|
+
Doc,
|
|
15
|
+
Fill,
|
|
16
|
+
IfBreak,
|
|
17
|
+
conditional_group,
|
|
18
|
+
group,
|
|
19
|
+
indent,
|
|
20
|
+
join,
|
|
21
|
+
print_doc,
|
|
22
|
+
)
|
|
23
|
+
from prettycsharp.printer.core import Core
|
|
24
|
+
from prettycsharp.syntax import Node
|
|
25
|
+
|
|
26
|
+
# Binary operator precedence, lowest first. Operators of one level chain.
|
|
27
|
+
_PRECEDENCE = {
|
|
28
|
+
"??": 1,
|
|
29
|
+
"||": 2,
|
|
30
|
+
"&&": 3,
|
|
31
|
+
"|": 4,
|
|
32
|
+
"^": 5,
|
|
33
|
+
"&": 6,
|
|
34
|
+
"==": 7,
|
|
35
|
+
"!=": 7,
|
|
36
|
+
"<": 8,
|
|
37
|
+
">": 8,
|
|
38
|
+
"<=": 8,
|
|
39
|
+
">=": 8,
|
|
40
|
+
"<<": 9,
|
|
41
|
+
">>": 9,
|
|
42
|
+
">>>": 9,
|
|
43
|
+
"+": 10,
|
|
44
|
+
"-": 10,
|
|
45
|
+
"*": 11,
|
|
46
|
+
"/": 11,
|
|
47
|
+
"%": 11,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Values that break across lines by themselves, so an assignment keeps them
|
|
51
|
+
# on its own line instead of breaking after the operator.
|
|
52
|
+
_SELF_BREAKING = frozenset(
|
|
53
|
+
{
|
|
54
|
+
"invocation_expression",
|
|
55
|
+
"object_creation_expression",
|
|
56
|
+
"implicit_object_creation_expression",
|
|
57
|
+
"anonymous_object_creation_expression",
|
|
58
|
+
"array_creation_expression",
|
|
59
|
+
"implicit_array_creation_expression",
|
|
60
|
+
"collection_expression",
|
|
61
|
+
"initializer_expression",
|
|
62
|
+
"lambda_expression",
|
|
63
|
+
"anonymous_method_expression",
|
|
64
|
+
"switch_expression",
|
|
65
|
+
"with_expression",
|
|
66
|
+
"conditional_expression",
|
|
67
|
+
"binary_expression",
|
|
68
|
+
"query_expression",
|
|
69
|
+
"stackalloc_expression",
|
|
70
|
+
"implicit_stackalloc_expression",
|
|
71
|
+
"tuple_expression",
|
|
72
|
+
"is_pattern_expression",
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Values that are laid out as a block of lines after the operator.
|
|
77
|
+
_BLOCK_LIKE = frozenset(
|
|
78
|
+
{
|
|
79
|
+
"switch_expression",
|
|
80
|
+
"anonymous_object_creation_expression",
|
|
81
|
+
"collection_expression",
|
|
82
|
+
"initializer_expression",
|
|
83
|
+
"lambda_expression",
|
|
84
|
+
"anonymous_method_expression",
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
_SIMPLE_VALUE_TYPES = frozenset(
|
|
89
|
+
{
|
|
90
|
+
"integer_literal",
|
|
91
|
+
"real_literal",
|
|
92
|
+
"string_literal",
|
|
93
|
+
"verbatim_string_literal",
|
|
94
|
+
"raw_string_literal",
|
|
95
|
+
"character_literal",
|
|
96
|
+
"boolean_literal",
|
|
97
|
+
"null_literal",
|
|
98
|
+
"identifier",
|
|
99
|
+
"predefined_type",
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
_CREATION_TYPES = frozenset(
|
|
104
|
+
{
|
|
105
|
+
"object_creation_expression",
|
|
106
|
+
"implicit_object_creation_expression",
|
|
107
|
+
"array_creation_expression",
|
|
108
|
+
"implicit_array_creation_expression",
|
|
109
|
+
"anonymous_object_creation_expression",
|
|
110
|
+
"with_expression",
|
|
111
|
+
"stackalloc_expression",
|
|
112
|
+
"implicit_stackalloc_expression",
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass
|
|
118
|
+
class _Link:
|
|
119
|
+
"""One step of a member chain: ``.Name`` or ``?.Name`` and what follows it."""
|
|
120
|
+
|
|
121
|
+
access: list[Node]
|
|
122
|
+
name: Node | None
|
|
123
|
+
suffixes: list[Node] = field(default_factory=list)
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def is_call(self) -> bool:
|
|
127
|
+
return any(s.type == "argument_list" for s in self.suffixes)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class Expressions(Core):
|
|
131
|
+
def block(self, block: Node, context: str) -> Doc: # pragma: no cover - Statements defines it
|
|
132
|
+
raise NotImplementedError
|
|
133
|
+
|
|
134
|
+
# Binary operators ---------------------------------------------------
|
|
135
|
+
|
|
136
|
+
def p_binary_expression(self, node: Node) -> Doc:
|
|
137
|
+
return self.binary(node, in_condition=False)
|
|
138
|
+
|
|
139
|
+
def binary(self, node: Node, *, in_condition: bool) -> Doc:
|
|
140
|
+
operands, operators = self._flatten_binary(node)
|
|
141
|
+
first = self.print(operands[0])
|
|
142
|
+
rest: list[Doc] = []
|
|
143
|
+
after = self.settings.binary_wrap == "after_operator"
|
|
144
|
+
for op, operand in zip(operators, operands[1:], strict=True):
|
|
145
|
+
if after:
|
|
146
|
+
rest.append([" ", self.tok(op), LINE, self.print(operand)])
|
|
147
|
+
else:
|
|
148
|
+
rest.append([LINE, self.tok(op), " ", self.print(operand)])
|
|
149
|
+
if in_condition:
|
|
150
|
+
return group([first, rest])
|
|
151
|
+
return group([first, indent(rest)])
|
|
152
|
+
|
|
153
|
+
def _flatten_binary(self, node: Node) -> tuple[list[Node], list[Node]]:
|
|
154
|
+
op = node.child("operator")
|
|
155
|
+
level = _PRECEDENCE.get(op.text or "", 0) if op is not None else 0
|
|
156
|
+
operands: list[Node] = []
|
|
157
|
+
operators: list[Node] = []
|
|
158
|
+
stack: list[Node] = [node]
|
|
159
|
+
while stack:
|
|
160
|
+
current = stack.pop()
|
|
161
|
+
current_op = current.child("operator") if current.type == "binary_expression" else None
|
|
162
|
+
if (
|
|
163
|
+
current_op is not None
|
|
164
|
+
and _PRECEDENCE.get(current_op.text or "", -1) == level
|
|
165
|
+
and not current.verbatim
|
|
166
|
+
):
|
|
167
|
+
left = current.child("left")
|
|
168
|
+
right = current.child("right")
|
|
169
|
+
if left is None or right is None:
|
|
170
|
+
operands.append(current)
|
|
171
|
+
continue
|
|
172
|
+
# Visit left, then the operator, then right.
|
|
173
|
+
stack.append(right)
|
|
174
|
+
stack.append(_OperatorMarker(current_op))
|
|
175
|
+
stack.append(left)
|
|
176
|
+
elif isinstance(current, _OperatorMarker):
|
|
177
|
+
operators.append(current.op)
|
|
178
|
+
else:
|
|
179
|
+
operands.append(current)
|
|
180
|
+
return operands, operators
|
|
181
|
+
|
|
182
|
+
# Conditional expressions ---------------------------------------------
|
|
183
|
+
|
|
184
|
+
def p_conditional_expression(self, node: Node) -> Doc:
|
|
185
|
+
condition = node.child("condition")
|
|
186
|
+
consequence = node.child("consequence")
|
|
187
|
+
alternative = node.child("alternative")
|
|
188
|
+
question = node.first_of("?")
|
|
189
|
+
colon = node.first_of(":")
|
|
190
|
+
if condition is None or consequence is None or alternative is None or question is None or colon is None:
|
|
191
|
+
return self.generic(node)
|
|
192
|
+
style = self.settings.ternary_wrap
|
|
193
|
+
cond = self.print(condition)
|
|
194
|
+
yes = self.print(consequence)
|
|
195
|
+
no = self.print(alternative)
|
|
196
|
+
if style == "after":
|
|
197
|
+
return group([cond, " ", self.tok(question), indent([LINE, yes, " ", self.tok(colon), LINE, no])])
|
|
198
|
+
return group(
|
|
199
|
+
[cond, indent([LINE, self.tok(question), " ", yes, LINE, self.tok(colon), " ", no])],
|
|
200
|
+
should_break=style == "always",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Assignment ----------------------------------------------------------
|
|
204
|
+
|
|
205
|
+
def p_assignment_expression(self, node: Node) -> Doc:
|
|
206
|
+
left = node.child("left")
|
|
207
|
+
op = node.child("operator")
|
|
208
|
+
right = node.child("right")
|
|
209
|
+
if left is None or op is None or right is None:
|
|
210
|
+
return self.generic(node)
|
|
211
|
+
return self.rhs(self.print(left), op, right)
|
|
212
|
+
|
|
213
|
+
def rhs(self, left: Doc, op: Node, value: Node) -> Doc:
|
|
214
|
+
"""``left op value``, breaking after the operator only for values that cannot break."""
|
|
215
|
+
# A comment at the very end of the value needs no break of its own.
|
|
216
|
+
after, value_doc = self.split_trailing(value, lambda: self.print(value))
|
|
217
|
+
if self.self_breaking(value):
|
|
218
|
+
return [group([left, " ", self.tok(op), " ", value_doc]), after]
|
|
219
|
+
return [group([left, " ", self.tok(op), group(indent([LINE, value_doc]))]), after]
|
|
220
|
+
|
|
221
|
+
def self_breaking(self, value: Node) -> bool:
|
|
222
|
+
if value.text is not None and ("\n" in value.text or "\r" in value.text):
|
|
223
|
+
# A multi-line string starts on the operator's line.
|
|
224
|
+
return True
|
|
225
|
+
if value.verbatim:
|
|
226
|
+
return False
|
|
227
|
+
kind = value.type
|
|
228
|
+
if kind in _SELF_BREAKING:
|
|
229
|
+
return True
|
|
230
|
+
if kind == "await_expression":
|
|
231
|
+
awaited = value.named_children()
|
|
232
|
+
return bool(awaited) and self.self_breaking(awaited[-1])
|
|
233
|
+
if kind in ("member_access_expression", "element_access_expression", "conditional_access_expression"):
|
|
234
|
+
return self.chain_calls(value) > 0
|
|
235
|
+
if kind == "cast_expression":
|
|
236
|
+
operand = value.child("value")
|
|
237
|
+
return operand is not None and self.self_breaking(operand)
|
|
238
|
+
return False
|
|
239
|
+
|
|
240
|
+
def expression_body(self, clause: Node) -> Doc:
|
|
241
|
+
"""`` => expression`` for a member or accessor, without the semicolon."""
|
|
242
|
+
arrow = clause.first_of("=>")
|
|
243
|
+
body = next((c for c in clause.children if c is not arrow), None)
|
|
244
|
+
if arrow is None or body is None:
|
|
245
|
+
return [" ", self.generic(clause)]
|
|
246
|
+
body_doc = self.print(body)
|
|
247
|
+
if self.block_like(body):
|
|
248
|
+
return [" ", self.tok(arrow), " ", body_doc]
|
|
249
|
+
if self.forced_chain(body):
|
|
250
|
+
if self.settings.expression_body_chain == "own_line":
|
|
251
|
+
return [" ", self.tok(arrow), indent([HARDLINE, body_doc])]
|
|
252
|
+
return [" ", self.tok(arrow), " ", body_doc]
|
|
253
|
+
return group([" ", self.tok(arrow), group(indent([LINE, body_doc]))])
|
|
254
|
+
|
|
255
|
+
def multiline_value(self, value: Node) -> bool:
|
|
256
|
+
"""Whether a value will take several lines whatever the width."""
|
|
257
|
+
if value.text is not None:
|
|
258
|
+
return "\n" in value.text or "\r" in value.text
|
|
259
|
+
if self.forced_chain(value):
|
|
260
|
+
return True
|
|
261
|
+
if value.type in ("lambda_expression", "anonymous_method_expression"):
|
|
262
|
+
body = value.child("body") or value.first_of("block")
|
|
263
|
+
return body is not None and (body.type == "block" or self.multiline_value(body))
|
|
264
|
+
if value.type == "switch_expression":
|
|
265
|
+
return True
|
|
266
|
+
if self.block_like(value):
|
|
267
|
+
return self.initializer_breaks(value) or value.type == "collection_expression" and self._collection_forced(value)
|
|
268
|
+
return False
|
|
269
|
+
|
|
270
|
+
def _collection_forced(self, node: Node) -> bool:
|
|
271
|
+
items, _ = self.items_and_commas(node)
|
|
272
|
+
if self.settings.simple_lists_inline and all(len(i) == 1 and _simple_value(_element_value(i[0])) for i in items):
|
|
273
|
+
return False
|
|
274
|
+
return self._forced_initializer([[_element_value(i[0])] for i in items if len(i) == 1])
|
|
275
|
+
|
|
276
|
+
def block_like(self, value: Node) -> bool:
|
|
277
|
+
if value.verbatim:
|
|
278
|
+
return False
|
|
279
|
+
if value.type in _BLOCK_LIKE:
|
|
280
|
+
return True
|
|
281
|
+
if value.type in _CREATION_TYPES:
|
|
282
|
+
return value.child("initializer") is not None or value.first_of("initializer_expression") is not None
|
|
283
|
+
return False
|
|
284
|
+
|
|
285
|
+
# Member chains -------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
def p_invocation_expression(self, node: Node) -> Doc:
|
|
288
|
+
return self.chain(node)
|
|
289
|
+
|
|
290
|
+
def p_member_access_expression(self, node: Node) -> Doc:
|
|
291
|
+
return self.chain(node)
|
|
292
|
+
|
|
293
|
+
def p_conditional_access_expression(self, node: Node) -> Doc:
|
|
294
|
+
return self.chain(node)
|
|
295
|
+
|
|
296
|
+
def p_element_access_expression(self, node: Node) -> Doc:
|
|
297
|
+
return self.chain(node)
|
|
298
|
+
|
|
299
|
+
def chain_calls(self, node: Node) -> int:
|
|
300
|
+
_, receiver_suffixes, links = self._flatten_chain(node)
|
|
301
|
+
del receiver_suffixes
|
|
302
|
+
return sum(1 for link in links if link.is_call)
|
|
303
|
+
|
|
304
|
+
def forced_chain(self, node: Node) -> bool:
|
|
305
|
+
threshold = self.settings.chain_break_threshold
|
|
306
|
+
if threshold <= 0 or node.type not in (
|
|
307
|
+
"invocation_expression",
|
|
308
|
+
"member_access_expression",
|
|
309
|
+
"conditional_access_expression",
|
|
310
|
+
"element_access_expression",
|
|
311
|
+
):
|
|
312
|
+
return False
|
|
313
|
+
return self.chain_calls(node) >= threshold
|
|
314
|
+
|
|
315
|
+
def _flatten_chain(self, node: Node) -> tuple[Node, list[Node], list[_Link]]:
|
|
316
|
+
links: list[_Link] = []
|
|
317
|
+
suffixes: list[Node] = []
|
|
318
|
+
current = node
|
|
319
|
+
while True:
|
|
320
|
+
if current.verbatim:
|
|
321
|
+
break
|
|
322
|
+
kind = current.type
|
|
323
|
+
if kind == "invocation_expression":
|
|
324
|
+
function = current.child("function")
|
|
325
|
+
arguments = current.child("arguments")
|
|
326
|
+
if function is None or arguments is None:
|
|
327
|
+
break
|
|
328
|
+
suffixes.append(arguments)
|
|
329
|
+
current = function
|
|
330
|
+
elif kind == "element_access_expression":
|
|
331
|
+
expression = current.child("expression")
|
|
332
|
+
subscript = current.child("subscript")
|
|
333
|
+
if expression is None or subscript is None:
|
|
334
|
+
break
|
|
335
|
+
suffixes.append(subscript)
|
|
336
|
+
current = expression
|
|
337
|
+
elif kind == "postfix_unary_expression" and current.children and current.children[-1].text == "!":
|
|
338
|
+
suffixes.append(current.children[-1])
|
|
339
|
+
current = current.children[0]
|
|
340
|
+
elif kind == "member_access_expression":
|
|
341
|
+
expression = current.child("expression")
|
|
342
|
+
name = current.child("name")
|
|
343
|
+
access = [c for c in current.children if c is not expression and c is not name]
|
|
344
|
+
if expression is None or name is None or len(access) != 1:
|
|
345
|
+
break
|
|
346
|
+
links.append(_Link(access, name, list(reversed(suffixes))))
|
|
347
|
+
suffixes = []
|
|
348
|
+
current = expression
|
|
349
|
+
elif kind == "conditional_access_expression":
|
|
350
|
+
condition = current.child("condition")
|
|
351
|
+
question = current.first_of("?")
|
|
352
|
+
binding = current.children[-1]
|
|
353
|
+
if condition is None or question is None or binding is condition or len(current.children) != 3:
|
|
354
|
+
break
|
|
355
|
+
if binding.type == "member_binding_expression" and len(binding.children) == 2:
|
|
356
|
+
links.append(_Link([question, binding.children[0]], binding.children[1], list(reversed(suffixes))))
|
|
357
|
+
elif binding.type == "element_binding_expression":
|
|
358
|
+
# ?[i]: an index on what comes before, behind its question mark.
|
|
359
|
+
links.append(_Link([question], None, [binding, *reversed(suffixes)]))
|
|
360
|
+
else:
|
|
361
|
+
break
|
|
362
|
+
suffixes = []
|
|
363
|
+
current = condition
|
|
364
|
+
else:
|
|
365
|
+
break
|
|
366
|
+
links.reverse()
|
|
367
|
+
return current, list(reversed(suffixes)), links
|
|
368
|
+
|
|
369
|
+
def chain(self, node: Node) -> Doc:
|
|
370
|
+
receiver, receiver_suffixes, links = self._flatten_chain(node)
|
|
371
|
+
if not links:
|
|
372
|
+
return self._call_or_access(receiver, receiver_suffixes, node)
|
|
373
|
+
head: list[Doc] = [self.print(receiver), *[self.suffix(s) for s in receiver_suffixes]]
|
|
374
|
+
calls = sum(1 for link in links if link.is_call)
|
|
375
|
+
threshold = self.settings.chain_break_threshold
|
|
376
|
+
# Property accesses before the first call stay on the receiver's line.
|
|
377
|
+
first_call = next((i for i, link in enumerate(links) if link.is_call), len(links))
|
|
378
|
+
for link in links[:first_call]:
|
|
379
|
+
head.append(self.link(link))
|
|
380
|
+
rest = links[first_call:]
|
|
381
|
+
if not rest:
|
|
382
|
+
return head
|
|
383
|
+
# Each group ends at a call; accesses after the last call join it.
|
|
384
|
+
groups: list[list[_Link]] = []
|
|
385
|
+
current: list[_Link] = []
|
|
386
|
+
for link in rest:
|
|
387
|
+
current.append(link)
|
|
388
|
+
if link.is_call:
|
|
389
|
+
groups.append(current)
|
|
390
|
+
current = []
|
|
391
|
+
if current:
|
|
392
|
+
groups[-1].extend(current)
|
|
393
|
+
group_docs = [[self.link(link) for link in g] for g in groups]
|
|
394
|
+
if threshold > 0 and calls >= threshold:
|
|
395
|
+
return [head, indent([[HARDLINE, g] for g in group_docs])]
|
|
396
|
+
if threshold <= 0 and len(groups) >= 2:
|
|
397
|
+
return group([head, indent([[SOFTLINE, g] for g in group_docs])])
|
|
398
|
+
return [head, *group_docs]
|
|
399
|
+
|
|
400
|
+
def _call_or_access(self, receiver: Node, suffixes: list[Node], node: Node) -> Doc:
|
|
401
|
+
if not suffixes:
|
|
402
|
+
if receiver is node:
|
|
403
|
+
return self.generic(node)
|
|
404
|
+
return self.print(receiver)
|
|
405
|
+
return [self.print(receiver), *[self.suffix(s) for s in suffixes]]
|
|
406
|
+
|
|
407
|
+
def link(self, link: _Link) -> Doc:
|
|
408
|
+
parts: list[Doc] = [self.tok(t) for t in link.access]
|
|
409
|
+
if link.name is not None:
|
|
410
|
+
parts.append(self.print(link.name))
|
|
411
|
+
parts.extend(self.suffix(s) for s in link.suffixes)
|
|
412
|
+
return parts
|
|
413
|
+
|
|
414
|
+
def suffix(self, node: Node) -> Doc:
|
|
415
|
+
if node.type == "argument_list":
|
|
416
|
+
return self.arguments(node)
|
|
417
|
+
return self.print(node)
|
|
418
|
+
|
|
419
|
+
# Arguments -----------------------------------------------------------
|
|
420
|
+
|
|
421
|
+
def p_argument_list(self, node: Node) -> Doc:
|
|
422
|
+
return self.arguments(node)
|
|
423
|
+
|
|
424
|
+
def p_bracketed_argument_list(self, node: Node) -> Doc:
|
|
425
|
+
return self.arguments(node)
|
|
426
|
+
|
|
427
|
+
def p_tuple_expression(self, node: Node) -> Doc:
|
|
428
|
+
return self.arguments(node)
|
|
429
|
+
|
|
430
|
+
def p_attribute_argument_list(self, node: Node) -> Doc:
|
|
431
|
+
return self.arguments(node)
|
|
432
|
+
|
|
433
|
+
def p_argument(self, node: Node) -> Doc:
|
|
434
|
+
# name: value, out var x, ref y: a space between parts, none before the colon.
|
|
435
|
+
parts: list[Doc] = []
|
|
436
|
+
for i, child in enumerate(node.children):
|
|
437
|
+
if i and child.text != ":":
|
|
438
|
+
parts.append(" ")
|
|
439
|
+
parts.append(self.print(child))
|
|
440
|
+
return parts
|
|
441
|
+
|
|
442
|
+
def p_attribute_argument(self, node: Node) -> Doc:
|
|
443
|
+
return self.p_argument(node)
|
|
444
|
+
|
|
445
|
+
def arguments(self, node: Node) -> Doc:
|
|
446
|
+
"""``(a, b)`` or ``[a, b]``: on one line, or one argument per line."""
|
|
447
|
+
if len(node.children) < 2:
|
|
448
|
+
return self.generic(node)
|
|
449
|
+
open_tok = node.children[0]
|
|
450
|
+
close_tok = node.children[-1]
|
|
451
|
+
items, commas = self.items_and_commas(node)
|
|
452
|
+
if not items:
|
|
453
|
+
return group([self.tok(open_tok), self.closing(close_tok, SOFTLINE)])
|
|
454
|
+
docs = [self.print_item(item) for item in items]
|
|
455
|
+
for i, comma in enumerate(commas):
|
|
456
|
+
if i < len(docs):
|
|
457
|
+
docs[i] = [docs[i], self.tok(comma)]
|
|
458
|
+
return self.argument_layout(open_tok, docs, close_tok, items)
|
|
459
|
+
|
|
460
|
+
def argument_layout(self, open_tok: Node, docs: list[Doc], close_tok: Node, items: Sequence[Sequence[Node]]) -> Doc:
|
|
461
|
+
style = self.settings.argument_wrap
|
|
462
|
+
open_doc = self.tok(open_tok)
|
|
463
|
+
hugging_close = self.closing(close_tok, "")
|
|
464
|
+
if style == "fill":
|
|
465
|
+
parts: list[Doc] = []
|
|
466
|
+
for i, doc in enumerate(docs):
|
|
467
|
+
if i:
|
|
468
|
+
parts.append(LINE)
|
|
469
|
+
parts.append(doc)
|
|
470
|
+
return group([open_doc, indent([SOFTLINE, Fill(parts)]), hugging_close])
|
|
471
|
+
if style == "align":
|
|
472
|
+
return group([open_doc, Align(-1, join(LINE, docs)), hugging_close])
|
|
473
|
+
if style == "one_per_line_close_on_last":
|
|
474
|
+
expanded: Doc = group([open_doc, indent([SOFTLINE, join(LINE, docs)]), hugging_close])
|
|
475
|
+
else:
|
|
476
|
+
expanded = group([open_doc, indent([SOFTLINE, join(LINE, docs)]), self.closing(close_tok, SOFTLINE)])
|
|
477
|
+
if self.settings.lambda_body == "hug" and not close_tok.leading and self._can_hug(items):
|
|
478
|
+
close_doc = self.tok(close_tok)
|
|
479
|
+
last = items[-1][0]
|
|
480
|
+
hugged_last = group(self.print(last), should_break=True)
|
|
481
|
+
if len(items) - 1 < len(docs) and isinstance(docs[-1], list) and len(docs[-1]) == 2:
|
|
482
|
+
# A trailing comma after the hugged argument stays with it.
|
|
483
|
+
hugged_last = group([hugged_last, docs[-1][1]], should_break=True)
|
|
484
|
+
head = docs[:-1]
|
|
485
|
+
flat = [open_doc, join(" ", docs), close_doc]
|
|
486
|
+
hugged = [open_doc, join(" ", [*head, hugged_last]), close_doc]
|
|
487
|
+
return conditional_group([flat, hugged, expanded])
|
|
488
|
+
return expanded
|
|
489
|
+
|
|
490
|
+
def _can_hug(self, items: Sequence[Sequence[Node]]) -> bool:
|
|
491
|
+
if not items or any(len(item) != 1 for item in items):
|
|
492
|
+
return False
|
|
493
|
+
last = items[-1][0]
|
|
494
|
+
target = last.children[-1] if last.type == "argument" and last.children else last
|
|
495
|
+
if not self.huggable(target):
|
|
496
|
+
return False
|
|
497
|
+
for item in items[:-1]:
|
|
498
|
+
node = item[0]
|
|
499
|
+
if any(t.leading or t.trailing for t in node.tokens()):
|
|
500
|
+
return False
|
|
501
|
+
if any(self.huggable(n) for n in _descendants(node)):
|
|
502
|
+
return False
|
|
503
|
+
return not any(t.leading for t in last.tokens())
|
|
504
|
+
|
|
505
|
+
def huggable(self, node: Node) -> bool:
|
|
506
|
+
if node.verbatim:
|
|
507
|
+
return False
|
|
508
|
+
if node.type in ("lambda_expression", "anonymous_method_expression"):
|
|
509
|
+
body = node.child("body") or node.first_of("block")
|
|
510
|
+
if body is None:
|
|
511
|
+
return False
|
|
512
|
+
# A block, or a body that takes several lines anyway, such as a
|
|
513
|
+
# broken member chain, hangs off the call's line.
|
|
514
|
+
return body.type == "block" or self.huggable(body) or self.forced_chain(body)
|
|
515
|
+
if node.type in _CREATION_TYPES:
|
|
516
|
+
return self.block_like(node) and self.initializer_breaks(node)
|
|
517
|
+
return node.type == "switch_expression"
|
|
518
|
+
|
|
519
|
+
def print_item(self, item: Sequence[Node]) -> Doc:
|
|
520
|
+
if len(item) == 1:
|
|
521
|
+
return self.print(item[0])
|
|
522
|
+
return self.generic_sequence(item)
|
|
523
|
+
|
|
524
|
+
def generic_sequence(self, nodes: Sequence[Node]) -> Doc:
|
|
525
|
+
parts: list[Doc] = []
|
|
526
|
+
previous: Node | None = None
|
|
527
|
+
for node in nodes:
|
|
528
|
+
if previous is not None and self.space_between(previous, node):
|
|
529
|
+
parts.append(" ")
|
|
530
|
+
parts.append(self.print(node))
|
|
531
|
+
previous = node
|
|
532
|
+
return parts
|
|
533
|
+
|
|
534
|
+
def items_and_commas(self, node: Node) -> tuple[list[list[Node]], list[Node]]:
|
|
535
|
+
items = self.separated(node)
|
|
536
|
+
return items, self.separators(node)
|
|
537
|
+
|
|
538
|
+
# Creation and initializers ---------------------------------------------
|
|
539
|
+
|
|
540
|
+
def p_object_creation_expression(self, node: Node) -> Doc:
|
|
541
|
+
return self.creation(node)
|
|
542
|
+
|
|
543
|
+
def p_implicit_object_creation_expression(self, node: Node) -> Doc:
|
|
544
|
+
return self.creation(node)
|
|
545
|
+
|
|
546
|
+
def p_array_creation_expression(self, node: Node) -> Doc:
|
|
547
|
+
return self.creation(node)
|
|
548
|
+
|
|
549
|
+
def p_implicit_array_creation_expression(self, node: Node) -> Doc:
|
|
550
|
+
return self.creation(node)
|
|
551
|
+
|
|
552
|
+
def p_stackalloc_expression(self, node: Node) -> Doc:
|
|
553
|
+
return self.creation(node)
|
|
554
|
+
|
|
555
|
+
def p_implicit_stackalloc_expression(self, node: Node) -> Doc:
|
|
556
|
+
return self.creation(node)
|
|
557
|
+
|
|
558
|
+
def creation(self, node: Node) -> Doc:
|
|
559
|
+
parts: list[Doc] = []
|
|
560
|
+
previous: Node | None = None
|
|
561
|
+
for child in node.children:
|
|
562
|
+
if child.type == "initializer_expression":
|
|
563
|
+
parts.append(self.initializer(child))
|
|
564
|
+
else:
|
|
565
|
+
if previous is not None and self._space_in_creation(previous, child):
|
|
566
|
+
parts.append(" ")
|
|
567
|
+
parts.append(self.print(child))
|
|
568
|
+
previous = child
|
|
569
|
+
return parts
|
|
570
|
+
|
|
571
|
+
def _space_in_creation(self, previous: Node, child: Node) -> bool:
|
|
572
|
+
if child.type in ("argument_list", "array_rank_specifier", "bracketed_argument_list"):
|
|
573
|
+
return False
|
|
574
|
+
if previous.text in ("new", "stackalloc"):
|
|
575
|
+
# new() and new[] stay closed up; new Foo takes a space.
|
|
576
|
+
return child.text not in ("(", "[") and child.type not in ("argument_list",)
|
|
577
|
+
return self.space_between(previous, child)
|
|
578
|
+
|
|
579
|
+
def initializer_breaks(self, node: Node) -> bool:
|
|
580
|
+
init = node if node.type == "initializer_expression" else (
|
|
581
|
+
node.child("initializer") or node.first_of("initializer_expression")
|
|
582
|
+
)
|
|
583
|
+
if init is None:
|
|
584
|
+
if node.type == "anonymous_object_creation_expression":
|
|
585
|
+
items = self.separated(_braced_part(node))
|
|
586
|
+
return len(items) >= max(self.settings.initializer_break_threshold, 1)
|
|
587
|
+
return False
|
|
588
|
+
items, _ = self.items_and_commas(init)
|
|
589
|
+
return self._forced_initializer(items)
|
|
590
|
+
|
|
591
|
+
def _forced_initializer(self, items: Sequence[Sequence[Node]]) -> bool:
|
|
592
|
+
threshold = self.settings.initializer_break_threshold
|
|
593
|
+
if not items:
|
|
594
|
+
return False
|
|
595
|
+
if self.settings.simple_lists_inline and all(len(i) == 1 and _simple_value(i[0]) for i in items):
|
|
596
|
+
return False
|
|
597
|
+
return threshold > 0 and len(items) >= threshold
|
|
598
|
+
|
|
599
|
+
def initializer(self, node: Node, *, leading_space: bool = True) -> Doc:
|
|
600
|
+
"""``{ a, b }`` after a creation: inline, or a braced block of lines."""
|
|
601
|
+
if len(node.children) < 2:
|
|
602
|
+
return self.generic(node)
|
|
603
|
+
open_tok = node.children[0]
|
|
604
|
+
close_tok = node.children[-1]
|
|
605
|
+
items, commas = self.items_and_commas(node)
|
|
606
|
+
if not items:
|
|
607
|
+
return group([" " if leading_space else "", self.tok(open_tok), self.closing(close_tok, LINE)])
|
|
608
|
+
docs = self._comma_docs(items, commas, may_add=True)
|
|
609
|
+
simple = self.settings.simple_lists_inline and all(len(i) == 1 and _simple_value(i[0]) for i in items)
|
|
610
|
+
force = self._forced_initializer(items)
|
|
611
|
+
# Allman: a broken initializer's brace takes a line of its own.
|
|
612
|
+
before: Doc = LINE if (self.settings.brace_style == "allman" and leading_space) else (" " if leading_space else "")
|
|
613
|
+
if simple:
|
|
614
|
+
body = self._packed(docs, items, commas)
|
|
615
|
+
return group([before, self.tok(open_tok), indent([LINE, body]), self.closing(close_tok, LINE)])
|
|
616
|
+
return group(
|
|
617
|
+
[before, self.tok(open_tok), indent([LINE, join(LINE, docs)]), self.closing(close_tok, LINE)],
|
|
618
|
+
should_break=force,
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
def _packed(self, docs: list[Doc], items: Sequence[Sequence[Node]], commas: Sequence[Node]) -> Doc:
|
|
622
|
+
"""Simple values as many to a line as fit; one to a line when any has a comment."""
|
|
623
|
+
nodes = [n for item in items for n in item] + list(commas)
|
|
624
|
+
if any(t.leading or t.trailing for n in nodes for t in n.tokens()):
|
|
625
|
+
return join(LINE, docs)
|
|
626
|
+
return Fill(_fill_parts(docs))
|
|
627
|
+
|
|
628
|
+
def _comma_docs(
|
|
629
|
+
self, items: Sequence[Sequence[Node]], commas: Sequence[Node], *, may_add: bool = False
|
|
630
|
+
) -> list[Doc]:
|
|
631
|
+
"""Each item with the comma after it; ``may_add`` lets trailing_commas = add add one."""
|
|
632
|
+
docs: list[Doc] = []
|
|
633
|
+
last = len(items) - 1
|
|
634
|
+
for i, item in enumerate(items):
|
|
635
|
+
if i < len(commas) and i < last:
|
|
636
|
+
docs.append([self.print_item(item), self.tok(commas[i])])
|
|
637
|
+
elif i < len(commas):
|
|
638
|
+
# A trailing comma that was kept: printed only when broken.
|
|
639
|
+
docs.append([self.print_item(item), IfBreak(self.tok(commas[i]), "")])
|
|
640
|
+
elif i == last and may_add and self.settings.trailing_commas == "add" and len(item) == 1:
|
|
641
|
+
# The added comma goes before a comment at the end of the item.
|
|
642
|
+
after, doc = self.split_trailing(item[0], functools.partial(self.print_item, item))
|
|
643
|
+
docs.append([doc, IfBreak(",", ""), after])
|
|
644
|
+
else:
|
|
645
|
+
docs.append(self.print_item(item))
|
|
646
|
+
return docs
|
|
647
|
+
|
|
648
|
+
def p_initializer_expression(self, node: Node) -> Doc:
|
|
649
|
+
# A nested initializer, such as B = { 1, 2 }, or one in an array.
|
|
650
|
+
return self.initializer(node, leading_space=False)
|
|
651
|
+
|
|
652
|
+
def p_collection_expression(self, node: Node) -> Doc:
|
|
653
|
+
if len(node.children) < 2:
|
|
654
|
+
return self.generic(node)
|
|
655
|
+
open_tok = node.children[0]
|
|
656
|
+
close_tok = node.children[-1]
|
|
657
|
+
items, commas = self.items_and_commas(node)
|
|
658
|
+
if not items:
|
|
659
|
+
return group([self.tok(open_tok), self.closing(close_tok, SOFTLINE)])
|
|
660
|
+
docs = self._comma_docs(items, commas, may_add=True)
|
|
661
|
+
simple = self.settings.simple_lists_inline and all(
|
|
662
|
+
len(i) == 1 and _simple_value(_element_value(i[0])) for i in items
|
|
663
|
+
)
|
|
664
|
+
if simple:
|
|
665
|
+
body = self._packed(docs, items, commas)
|
|
666
|
+
return group([self.tok(open_tok), indent([SOFTLINE, body]), self.closing(close_tok, SOFTLINE)])
|
|
667
|
+
force = self._forced_initializer([[_element_value(i[0])] for i in items if len(i) == 1])
|
|
668
|
+
return group(
|
|
669
|
+
[SOFTLINE, self.tok(open_tok), indent([LINE, join(LINE, docs)]), self.closing(close_tok, LINE)],
|
|
670
|
+
should_break=force,
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
def p_collection_element(self, node: Node) -> Doc:
|
|
674
|
+
return self.generic(node)
|
|
675
|
+
|
|
676
|
+
def p_anonymous_object_creation_expression(self, node: Node) -> Doc:
|
|
677
|
+
new = node.first_of("new")
|
|
678
|
+
open_tok = node.first_of("{")
|
|
679
|
+
close_tok = node.children[-1] if node.children else None
|
|
680
|
+
if new is None or open_tok is None or close_tok is None or close_tok.text != "}":
|
|
681
|
+
return self.generic(node)
|
|
682
|
+
braced = _braced_part(node)
|
|
683
|
+
items, commas = self.items_and_commas(braced)
|
|
684
|
+
if not items:
|
|
685
|
+
return [self.tok(new), " ", self.tok(open_tok), " ", self.tok(close_tok)]
|
|
686
|
+
docs = self._comma_docs(items, commas, may_add=True)
|
|
687
|
+
threshold = self.settings.initializer_break_threshold
|
|
688
|
+
force = threshold > 0 and len(items) >= threshold
|
|
689
|
+
before: Doc = LINE if self.settings.brace_style == "allman" else " "
|
|
690
|
+
return group(
|
|
691
|
+
[self.tok(new), before, self.tok(open_tok), indent([LINE, join(LINE, docs)]), self.closing(close_tok, LINE)],
|
|
692
|
+
should_break=force,
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
def p_with_expression(self, node: Node) -> Doc:
|
|
696
|
+
open_tok = node.first_of("{")
|
|
697
|
+
close_tok = node.children[-1] if node.children else None
|
|
698
|
+
with_tok = node.first_of("with")
|
|
699
|
+
if open_tok is None or close_tok is None or with_tok is None or close_tok.text != "}":
|
|
700
|
+
return self.generic(node)
|
|
701
|
+
expression = node.children[0]
|
|
702
|
+
braced = _braced_part(node)
|
|
703
|
+
items, commas = self.items_and_commas(braced)
|
|
704
|
+
# with_initializer wraps the assignments in some grammar versions.
|
|
705
|
+
if len(items) == 1 and len(items[0]) == 1 and items[0][0].type == "with_initializer":
|
|
706
|
+
inner = items[0][0]
|
|
707
|
+
items = [[c for c in group_] for group_ in _split_commas(inner.children)]
|
|
708
|
+
commas = [c for c in inner.children if c.text == "," and not c.named]
|
|
709
|
+
head = [self.print(expression), " ", self.tok(with_tok)]
|
|
710
|
+
if not items:
|
|
711
|
+
return group([head, " ", self.tok(open_tok), self.closing(close_tok, LINE)])
|
|
712
|
+
docs = self._comma_docs(items, commas, may_add=True)
|
|
713
|
+
force = self._forced_initializer(items)
|
|
714
|
+
before: Doc = LINE if self.settings.brace_style == "allman" else " "
|
|
715
|
+
return group([head, before, self.tok(open_tok), indent([LINE, join(LINE, docs)]), self.closing(close_tok, LINE)], should_break=force)
|
|
716
|
+
|
|
717
|
+
def p_with_initializer(self, node: Node) -> Doc:
|
|
718
|
+
return self.generic_sequence(node.children)
|
|
719
|
+
|
|
720
|
+
# Lambdas -------------------------------------------------------------
|
|
721
|
+
|
|
722
|
+
def p_lambda_expression(self, node: Node) -> Doc:
|
|
723
|
+
arrow = node.first_of("=>")
|
|
724
|
+
body = node.child("body")
|
|
725
|
+
if arrow is None or body is None:
|
|
726
|
+
return self.generic(node)
|
|
727
|
+
head: list[Doc] = []
|
|
728
|
+
previous: Node | None = None
|
|
729
|
+
for child in node.children:
|
|
730
|
+
if child is arrow:
|
|
731
|
+
break
|
|
732
|
+
if previous is not None:
|
|
733
|
+
head.append(" ")
|
|
734
|
+
head.append(self.print(child))
|
|
735
|
+
previous = child
|
|
736
|
+
if body.type == "block":
|
|
737
|
+
return [head, " ", self.tok(arrow), self.block(body, "lambda")]
|
|
738
|
+
body_doc = self.print(body)
|
|
739
|
+
if self.self_breaking(body) or self.block_like(body):
|
|
740
|
+
return [head, " ", self.tok(arrow), " ", body_doc]
|
|
741
|
+
return group([head, " ", self.tok(arrow), group(indent([LINE, body_doc]))])
|
|
742
|
+
|
|
743
|
+
def p_anonymous_method_expression(self, node: Node) -> Doc:
|
|
744
|
+
block = node.first_of("block")
|
|
745
|
+
if block is None or node.children[-1] is not block:
|
|
746
|
+
return self.generic(node)
|
|
747
|
+
head = self.generic_sequence(node.children[:-1])
|
|
748
|
+
return [head, self.block(block, "lambda")]
|
|
749
|
+
|
|
750
|
+
# Switch expressions ---------------------------------------------------
|
|
751
|
+
|
|
752
|
+
def p_switch_expression(self, node: Node) -> Doc:
|
|
753
|
+
switch = node.first_of("switch")
|
|
754
|
+
open_tok = node.first_of("{")
|
|
755
|
+
close_tok = node.children[-1] if node.children else None
|
|
756
|
+
if switch is None or open_tok is None or close_tok is None or close_tok.text != "}":
|
|
757
|
+
return self.generic(node)
|
|
758
|
+
governing = node.children[0]
|
|
759
|
+
arms = [c for c in node.children if c.type == "switch_expression_arm"]
|
|
760
|
+
commas = [c for c in node.children if c.text == "," and not c.named]
|
|
761
|
+
head = [self.print(governing), " ", self.tok(switch)]
|
|
762
|
+
if not arms:
|
|
763
|
+
return group([head, " ", self.tok(open_tok), self.closing(close_tok, LINE)])
|
|
764
|
+
comments: list[Doc] = []
|
|
765
|
+
lefts: list[Doc] = []
|
|
766
|
+
rights: list[Doc] = []
|
|
767
|
+
widths: list[int | None] = []
|
|
768
|
+
for arm in arms:
|
|
769
|
+
above, (left, right) = self.split_leading(arm, functools.partial(self._arm_parts, arm))
|
|
770
|
+
comments.append(above)
|
|
771
|
+
lefts.append(left)
|
|
772
|
+
rights.append(right)
|
|
773
|
+
widths.append(self.flat_width(left) if self.settings.align_switch_arms else None)
|
|
774
|
+
known = [w for w in widths if w is not None]
|
|
775
|
+
target = max(known) if known else 0
|
|
776
|
+
lines: list[Doc] = []
|
|
777
|
+
for i in range(len(arms)):
|
|
778
|
+
pad = ""
|
|
779
|
+
if widths[i] is not None and len(known) > 1:
|
|
780
|
+
pad = " " * (target - (widths[i] or 0))
|
|
781
|
+
doc: list[Doc] = [comments[i], lefts[i], pad, rights[i]]
|
|
782
|
+
comma = commas[i] if i < len(commas) else None
|
|
783
|
+
if comma is not None and (i < len(arms) - 1 or self.settings.trailing_commas != "remove"):
|
|
784
|
+
doc.append(self.tok(comma))
|
|
785
|
+
elif comma is None and i < len(arms) - 1:
|
|
786
|
+
doc.append(",")
|
|
787
|
+
lines.append(doc)
|
|
788
|
+
return [
|
|
789
|
+
head,
|
|
790
|
+
self.open_brace("statement"),
|
|
791
|
+
self.tok(open_tok),
|
|
792
|
+
indent([HARDLINE, join(HARDLINE, lines)]),
|
|
793
|
+
self.close_comments(close_tok),
|
|
794
|
+
HARDLINE,
|
|
795
|
+
self.tok(close_tok, leading=False),
|
|
796
|
+
]
|
|
797
|
+
|
|
798
|
+
def _arm_parts(self, arm: Node) -> tuple[Doc, Doc]:
|
|
799
|
+
arrow = arm.first_of("=>")
|
|
800
|
+
if arrow is None:
|
|
801
|
+
return self.generic(arm), ""
|
|
802
|
+
index = arm.children.index(arrow)
|
|
803
|
+
left = self.generic_sequence(arm.children[:index])
|
|
804
|
+
value = arm.children[index + 1 :]
|
|
805
|
+
if len(value) != 1:
|
|
806
|
+
return left, [" ", self.tok(arrow), " ", self.generic_sequence(value)]
|
|
807
|
+
value_node = value[0]
|
|
808
|
+
if self.self_breaking(value_node) or self.block_like(value_node):
|
|
809
|
+
return left, [" ", self.tok(arrow), " ", self.print(value_node)]
|
|
810
|
+
return left, group([" ", self.tok(arrow), group(indent([LINE, self.print(value_node)]))])
|
|
811
|
+
|
|
812
|
+
def flat_width(self, doc: Doc) -> int | None:
|
|
813
|
+
"""The width of a document printed on one line, or None if it cannot be."""
|
|
814
|
+
text = print_doc(doc, 1_000_000)
|
|
815
|
+
if "\n" in text or "\r" in text:
|
|
816
|
+
return None
|
|
817
|
+
return len(text)
|
|
818
|
+
|
|
819
|
+
# Operators and small forms ---------------------------------------------
|
|
820
|
+
|
|
821
|
+
def p_prefix_unary_expression(self, node: Node) -> Doc:
|
|
822
|
+
if len(node.children) != 2:
|
|
823
|
+
return self.generic(node)
|
|
824
|
+
op, operand = node.children
|
|
825
|
+
return self.join_tokens([(op, self.tok(op)), (operand, self.print(operand))])
|
|
826
|
+
|
|
827
|
+
def p_postfix_unary_expression(self, node: Node) -> Doc:
|
|
828
|
+
if len(node.children) != 2:
|
|
829
|
+
return self.generic(node)
|
|
830
|
+
operand, op = node.children
|
|
831
|
+
if op.text == "!":
|
|
832
|
+
return self.chain(node)
|
|
833
|
+
return self.join_tokens([(operand, self.print(operand)), (op, self.tok(op))])
|
|
834
|
+
|
|
835
|
+
def p_cast_expression(self, node: Node) -> Doc:
|
|
836
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
837
|
+
|
|
838
|
+
def p_parenthesized_expression(self, node: Node) -> Doc:
|
|
839
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
840
|
+
|
|
841
|
+
def p_await_expression(self, node: Node) -> Doc:
|
|
842
|
+
return self.spaced(node.children)
|
|
843
|
+
|
|
844
|
+
def p_throw_expression(self, node: Node) -> Doc:
|
|
845
|
+
return self.spaced(node.children)
|
|
846
|
+
|
|
847
|
+
def p_ref_expression(self, node: Node) -> Doc:
|
|
848
|
+
return self.spaced(node.children)
|
|
849
|
+
|
|
850
|
+
def p_declaration_expression(self, node: Node) -> Doc:
|
|
851
|
+
return self.spaced(node.children)
|
|
852
|
+
|
|
853
|
+
def p_as_expression(self, node: Node) -> Doc:
|
|
854
|
+
return self.spaced(node.children)
|
|
855
|
+
|
|
856
|
+
def p_is_expression(self, node: Node) -> Doc:
|
|
857
|
+
return self.spaced(node.children)
|
|
858
|
+
|
|
859
|
+
def p_is_pattern_expression(self, node: Node) -> Doc:
|
|
860
|
+
return self.spaced(node.children)
|
|
861
|
+
|
|
862
|
+
def p_range_expression(self, node: Node) -> Doc:
|
|
863
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
864
|
+
|
|
865
|
+
def p_typeof_expression(self, node: Node) -> Doc:
|
|
866
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
867
|
+
|
|
868
|
+
def p_sizeof_expression(self, node: Node) -> Doc:
|
|
869
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
870
|
+
|
|
871
|
+
def p_default_expression(self, node: Node) -> Doc:
|
|
872
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
873
|
+
|
|
874
|
+
def p_checked_expression(self, node: Node) -> Doc:
|
|
875
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
876
|
+
|
|
877
|
+
def p_makeref_expression(self, node: Node) -> Doc:
|
|
878
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
879
|
+
|
|
880
|
+
def p_member_binding_expression(self, node: Node) -> Doc:
|
|
881
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
882
|
+
|
|
883
|
+
def p_element_binding_expression(self, node: Node) -> Doc:
|
|
884
|
+
return self.arguments(node)
|
|
885
|
+
|
|
886
|
+
# Patterns ------------------------------------------------------------
|
|
887
|
+
|
|
888
|
+
def p_property_pattern_clause(self, node: Node) -> Doc:
|
|
889
|
+
if len(node.children) < 2:
|
|
890
|
+
return self.generic(node)
|
|
891
|
+
open_tok = node.children[0]
|
|
892
|
+
close_tok = node.children[-1]
|
|
893
|
+
items, commas = self.items_and_commas(node)
|
|
894
|
+
if not items:
|
|
895
|
+
return group([self.tok(open_tok), self.closing(close_tok, LINE)])
|
|
896
|
+
docs = self._comma_docs(items, commas)
|
|
897
|
+
return group([self.tok(open_tok), indent([LINE, join(LINE, docs)]), self.closing(close_tok, LINE)])
|
|
898
|
+
|
|
899
|
+
def p_positional_pattern_clause(self, node: Node) -> Doc:
|
|
900
|
+
return self.arguments(node)
|
|
901
|
+
|
|
902
|
+
def p_list_pattern(self, node: Node) -> Doc:
|
|
903
|
+
return self.arguments(node)
|
|
904
|
+
|
|
905
|
+
def p_tuple_pattern(self, node: Node) -> Doc:
|
|
906
|
+
return self.arguments(node)
|
|
907
|
+
|
|
908
|
+
def p_parenthesized_variable_designation(self, node: Node) -> Doc:
|
|
909
|
+
return self.arguments(node)
|
|
910
|
+
|
|
911
|
+
def p_subpattern(self, node: Node) -> Doc:
|
|
912
|
+
colon = node.first_of(":")
|
|
913
|
+
if colon is None:
|
|
914
|
+
return self.generic(node)
|
|
915
|
+
index = node.children.index(colon)
|
|
916
|
+
return [
|
|
917
|
+
self.generic_sequence(node.children[:index]),
|
|
918
|
+
self.tok(colon),
|
|
919
|
+
" ",
|
|
920
|
+
self.generic_sequence(node.children[index + 1 :]),
|
|
921
|
+
]
|
|
922
|
+
|
|
923
|
+
def p_relational_pattern(self, node: Node) -> Doc:
|
|
924
|
+
return self.spaced(node.children)
|
|
925
|
+
|
|
926
|
+
def p_and_pattern(self, node: Node) -> Doc:
|
|
927
|
+
return self.spaced(node.children)
|
|
928
|
+
|
|
929
|
+
def p_or_pattern(self, node: Node) -> Doc:
|
|
930
|
+
return self.spaced(node.children)
|
|
931
|
+
|
|
932
|
+
def p_negated_pattern(self, node: Node) -> Doc:
|
|
933
|
+
return self.spaced(node.children)
|
|
934
|
+
|
|
935
|
+
def p_declaration_pattern(self, node: Node) -> Doc:
|
|
936
|
+
return self.spaced(node.children)
|
|
937
|
+
|
|
938
|
+
def p_recursive_pattern(self, node: Node) -> Doc:
|
|
939
|
+
parts: list[Doc] = []
|
|
940
|
+
previous: Node | None = None
|
|
941
|
+
for child in node.children:
|
|
942
|
+
if previous is not None and not (child.type == "positional_pattern_clause" and previous.named):
|
|
943
|
+
parts.append(" ")
|
|
944
|
+
parts.append(self.print(child))
|
|
945
|
+
previous = child
|
|
946
|
+
return parts
|
|
947
|
+
|
|
948
|
+
def p_var_pattern(self, node: Node) -> Doc:
|
|
949
|
+
return self.spaced(node.children)
|
|
950
|
+
|
|
951
|
+
def p_when_clause(self, node: Node) -> Doc:
|
|
952
|
+
return self.spaced(node.children)
|
|
953
|
+
|
|
954
|
+
# Queries -------------------------------------------------------------
|
|
955
|
+
|
|
956
|
+
def p_query_expression(self, node: Node) -> Doc:
|
|
957
|
+
segments: list[list[Node]] = []
|
|
958
|
+
for child in node.children:
|
|
959
|
+
# A clause starts a line; `into name` continues the clause before it.
|
|
960
|
+
if child.type.endswith("_clause") or not segments:
|
|
961
|
+
segments.append([child])
|
|
962
|
+
else:
|
|
963
|
+
segments[-1].append(child)
|
|
964
|
+
docs = [self.generic_sequence(segment) for segment in segments]
|
|
965
|
+
if len(docs) < 2:
|
|
966
|
+
return docs
|
|
967
|
+
return group([docs[0], indent([[LINE, d] for d in docs[1:]])])
|
|
968
|
+
|
|
969
|
+
# Names and types ------------------------------------------------------
|
|
970
|
+
|
|
971
|
+
def p_generic_name(self, node: Node) -> Doc:
|
|
972
|
+
return [self.print(c) for c in node.children]
|
|
973
|
+
|
|
974
|
+
def p_type_argument_list(self, node: Node) -> Doc:
|
|
975
|
+
return self.angle_list(node)
|
|
976
|
+
|
|
977
|
+
def p_type_parameter_list(self, node: Node) -> Doc:
|
|
978
|
+
return self.angle_list(node)
|
|
979
|
+
|
|
980
|
+
def angle_list(self, node: Node) -> Doc:
|
|
981
|
+
if len(node.children) < 2:
|
|
982
|
+
return self.generic(node)
|
|
983
|
+
inner = node.children[1:-1]
|
|
984
|
+
if all(c.text == "," and not c.named for c in inner):
|
|
985
|
+
# An open generic type, typeof(Dictionary<,>): only commas.
|
|
986
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
987
|
+
items, commas = self.items_and_commas(node)
|
|
988
|
+
docs: list[Doc] = []
|
|
989
|
+
for i, item in enumerate(items):
|
|
990
|
+
doc = self.print_item(item)
|
|
991
|
+
if i < len(commas):
|
|
992
|
+
doc = [doc, self.tok(commas[i])]
|
|
993
|
+
docs.append(doc)
|
|
994
|
+
return [self.tok(node.children[0]), join(" ", docs), self.tok(node.children[-1])]
|
|
995
|
+
|
|
996
|
+
def p_type_parameter(self, node: Node) -> Doc:
|
|
997
|
+
return self.spaced(node.children)
|
|
998
|
+
|
|
999
|
+
def p_qualified_name(self, node: Node) -> Doc:
|
|
1000
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
1001
|
+
|
|
1002
|
+
def p_alias_qualified_name(self, node: Node) -> Doc:
|
|
1003
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
1004
|
+
|
|
1005
|
+
def p_array_type(self, node: Node) -> Doc:
|
|
1006
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
1007
|
+
|
|
1008
|
+
def p_array_rank_specifier(self, node: Node) -> Doc:
|
|
1009
|
+
parts: list[Doc] = []
|
|
1010
|
+
for child in node.children:
|
|
1011
|
+
parts.append(self.print(child))
|
|
1012
|
+
if child.text == "," and child is not node.children[-2] and node.children[node.children.index(child) + 1].text not in (",", "]"):
|
|
1013
|
+
parts.append(" ")
|
|
1014
|
+
return parts
|
|
1015
|
+
|
|
1016
|
+
def p_nullable_type(self, node: Node) -> Doc:
|
|
1017
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
1018
|
+
|
|
1019
|
+
def p_pointer_type(self, node: Node) -> Doc:
|
|
1020
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
1021
|
+
|
|
1022
|
+
def p_tuple_type(self, node: Node) -> Doc:
|
|
1023
|
+
return self.arguments(node)
|
|
1024
|
+
|
|
1025
|
+
def p_tuple_element(self, node: Node) -> Doc:
|
|
1026
|
+
return self.spaced(node.children)
|
|
1027
|
+
|
|
1028
|
+
def p_ref_type(self, node: Node) -> Doc:
|
|
1029
|
+
return self.spaced(node.children)
|
|
1030
|
+
|
|
1031
|
+
def p_scoped_type(self, node: Node) -> Doc:
|
|
1032
|
+
return self.spaced(node.children)
|
|
1033
|
+
|
|
1034
|
+
def p_explicit_interface_specifier(self, node: Node) -> Doc:
|
|
1035
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
1036
|
+
|
|
1037
|
+
def p_expression_element(self, node: Node) -> Doc:
|
|
1038
|
+
return self.generic(node)
|
|
1039
|
+
|
|
1040
|
+
def p_spread_element(self, node: Node) -> Doc:
|
|
1041
|
+
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
class _OperatorMarker(Node):
|
|
1045
|
+
"""A placeholder in the binary-expression flattening stack."""
|
|
1046
|
+
|
|
1047
|
+
def __init__(self, op: Node) -> None:
|
|
1048
|
+
super().__init__(type="__operator__")
|
|
1049
|
+
self.op = op
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
def _descendants(node: Node) -> list[Node]:
|
|
1053
|
+
out: list[Node] = []
|
|
1054
|
+
stack = list(node.children)
|
|
1055
|
+
while stack:
|
|
1056
|
+
current = stack.pop()
|
|
1057
|
+
out.append(current)
|
|
1058
|
+
stack.extend(current.children)
|
|
1059
|
+
return out
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
def _simple_value(node: Node) -> bool:
|
|
1063
|
+
if node.type in _SIMPLE_VALUE_TYPES:
|
|
1064
|
+
return True
|
|
1065
|
+
if node.type == "prefix_unary_expression" and len(node.children) == 2:
|
|
1066
|
+
return node.children[0].text in ("-", "+") and node.children[1].type in ("integer_literal", "real_literal")
|
|
1067
|
+
if node.type in ("member_access_expression", "qualified_name"):
|
|
1068
|
+
return all(c.type in ("identifier", "member_access_expression", "qualified_name") or c.text == "." for c in node.children)
|
|
1069
|
+
return False
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
def _element_value(node: Node) -> Node:
|
|
1073
|
+
# collection_element > expression_element > value; ..value spreads a value.
|
|
1074
|
+
while True:
|
|
1075
|
+
if node.type in ("collection_element", "expression_element") and len(node.children) == 1:
|
|
1076
|
+
node = node.children[0]
|
|
1077
|
+
elif node.type == "spread_element" and len(node.children) == 2:
|
|
1078
|
+
node = node.children[1]
|
|
1079
|
+
else:
|
|
1080
|
+
return node
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
def _fill_parts(docs: Sequence[Doc]) -> list[Doc]:
|
|
1084
|
+
parts: list[Doc] = []
|
|
1085
|
+
for i, doc in enumerate(docs):
|
|
1086
|
+
if i:
|
|
1087
|
+
parts.append(LINE)
|
|
1088
|
+
parts.append(doc)
|
|
1089
|
+
return parts
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
def _braced_part(node: Node) -> Node:
|
|
1093
|
+
"""A stand-in node holding ``{`` ... ``}`` of a node, for splitting at commas."""
|
|
1094
|
+
open_index = next(i for i, c in enumerate(node.children) if c.text == "{")
|
|
1095
|
+
holder = Node(type="braced")
|
|
1096
|
+
holder.children = node.children[open_index:]
|
|
1097
|
+
return holder
|
|
1098
|
+
|
|
1099
|
+
|
|
1100
|
+
def _split_commas(children: Sequence[Node]) -> list[list[Node]]:
|
|
1101
|
+
groups: list[list[Node]] = [[]]
|
|
1102
|
+
for child in children:
|
|
1103
|
+
if child.text == "," and not child.named:
|
|
1104
|
+
groups.append([])
|
|
1105
|
+
else:
|
|
1106
|
+
groups[-1].append(child)
|
|
1107
|
+
return [g for g in groups if g]
|