prettycsharp 1.1.2__tar.gz → 1.1.4__tar.gz
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-1.1.2 → prettycsharp-1.1.4}/CHANGELOG.md +13 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/DESIGN.md +1 -1
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/PKG-INFO +1 -1
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/__init__.py +1 -1
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/printer/core.py +21 -1
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/printer/declarations.py +23 -14
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/printer/expressions.py +4 -2
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/printer/statements.py +9 -4
- prettycsharp-1.1.4/tests/fuzz_corpus/csharp/comment_only_accessor_list.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_layout.py +104 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.gitattributes +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/codeql/codeql-config.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/dependabot.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/build.in +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/build.txt +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/fuzz.in +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/fuzz.txt +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/pip-audit.in +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/pip-audit.txt +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/runtime.in +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/runtime.txt +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/test.in +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/test.txt +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/zizmor.in +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/requirements/zizmor.txt +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/security/accepted.toml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/security/clamav/Dockerfile +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/security/malware-accepted.toml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/security/semgrep/Dockerfile +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/security/yara.json +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/security/yara_update.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/workflows/auto-merge.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/workflows/ci.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/workflows/fuzz.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/workflows/malware-scan.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/workflows/publish.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/workflows/scorecard.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/workflows/security.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/workflows/update-yara-rules.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.github/zizmor.yml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/.gitignore +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/AGENTS.md +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/CLAUDE.md +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/LICENSE +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/README.md +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/SECURITY.md +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/fuzz/fuzz_formatter.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/pyproject.toml +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/scripts/security/malware_report.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/scripts/security/security_report.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/__main__.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/cli.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/conditional.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/config.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/doc.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/formatter.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/ignore.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/isolate.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/parsing.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/printer/__init__.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/project.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/py.typed +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/rewrites.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/settings.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/syntax.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/src/prettycsharp/verify.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/declarations.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/directives.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/embedded_statements.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/expressions.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/members.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/order.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/queries_and_patterns.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/split_by_conditionals.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/statements.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/fuzz_corpus/csharp/strings.cs +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/support.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_cli.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_conditional.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_config.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_doc.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_docs.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_format_around.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_fuzz_corpus.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_ignore.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_parsing.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_project.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_rewrites.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_settings.py +0 -0
- {prettycsharp-1.1.2 → prettycsharp-1.1.4}/tests/test_verify.py +0 -0
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Each release's section becomes the body of its GitHub release.
|
|
4
4
|
|
|
5
|
+
## [1.1.4] - 2026-10-17
|
|
6
|
+
|
|
7
|
+
- An empty body `{ }` after a header that spans lines goes on its own line,
|
|
8
|
+
as Visual Studio formats it. Before, `) { }` after a broken parameter
|
|
9
|
+
list, constructor initializer, `where` clause, base list, catch filter or
|
|
10
|
+
lambda parameter list was flagged by IDE0055. A header on one line keeps
|
|
11
|
+
`{ }` beside it.
|
|
12
|
+
|
|
13
|
+
## [1.1.3] - 2026-10-17
|
|
14
|
+
|
|
15
|
+
- A property's accessor list or an enum that holds only a comment no longer
|
|
16
|
+
gains a blank line each time the file is formatted. Found by fuzzing.
|
|
17
|
+
|
|
5
18
|
## [1.1.2] - 2026-10-17
|
|
6
19
|
|
|
7
20
|
- Nothing is padded into columns by default any more: `align_assignments`,
|
|
@@ -104,7 +104,7 @@ stops the search, and the file nearest the source wins.
|
|
|
104
104
|
| `brace_style` | `allman` | `kr`, `mixed` (Allman for types and members, K&R in method bodies) |
|
|
105
105
|
| `continuation_keywords` | `blank_line`: `else`, `else if`, `catch`, `finally`, and a `do` loop's `while` go on their own line after a blank line | `new_line`, `same_line` |
|
|
106
106
|
| `braces` | `preserve` | `always`, `single_line_only` |
|
|
107
|
-
| `empty_bodies` | `inline`: `{ }` on the declaration line | `allman` |
|
|
107
|
+
| `empty_bodies` | `inline`: `{ }` on the declaration line, or on its own line when the header spans lines | `allman` |
|
|
108
108
|
|
|
109
109
|
Short auto-property accessors always stay on one line: `public int Id { get; set; }`.
|
|
110
110
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: prettycsharp
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.4
|
|
4
4
|
Summary: An opinionated formatter for C#, in the spirit of Prettier
|
|
5
5
|
Project-URL: Homepage, https://github.com/WilliamSmithEdward/prettyCSharp
|
|
6
6
|
Project-URL: Repository, https://github.com/WilliamSmithEdward/prettyCSharp
|
|
@@ -4,7 +4,7 @@ from prettycsharp.formatter import FormatResult, FormattingError, format_details
|
|
|
4
4
|
from prettycsharp.parsing import CSharpSyntaxError
|
|
5
5
|
from prettycsharp.settings import Settings, SettingsError
|
|
6
6
|
|
|
7
|
-
__version__ = "1.1.
|
|
7
|
+
__version__ = "1.1.4"
|
|
8
8
|
|
|
9
9
|
__all__ = [
|
|
10
10
|
"CSharpSyntaxError",
|
|
@@ -22,6 +22,7 @@ from prettycsharp.doc import (
|
|
|
22
22
|
TRIM_INDENT,
|
|
23
23
|
Doc,
|
|
24
24
|
Group,
|
|
25
|
+
IfBreak,
|
|
25
26
|
LineSuffix,
|
|
26
27
|
Wrapped,
|
|
27
28
|
group,
|
|
@@ -90,6 +91,8 @@ class Core:
|
|
|
90
91
|
self.file = file
|
|
91
92
|
self.lang = lang_version
|
|
92
93
|
self._blank_stack: list[Callable[[Node, Node], bool]] = []
|
|
94
|
+
# Tokens whose leading comments were printed ahead of them.
|
|
95
|
+
self._hoisted: set[int] = set()
|
|
93
96
|
self._handlers: dict[str, Handler] = {}
|
|
94
97
|
for name in dir(self):
|
|
95
98
|
if name.startswith("p_"):
|
|
@@ -115,7 +118,7 @@ class Core:
|
|
|
115
118
|
if t.text is None:
|
|
116
119
|
return self.print(t)
|
|
117
120
|
parts: list[Doc] = []
|
|
118
|
-
if leading and t.leading:
|
|
121
|
+
if leading and t.leading and id(t) not in self._hoisted:
|
|
119
122
|
parts.append(self.leading_comments(t))
|
|
120
123
|
parts.append(self.token_text(t))
|
|
121
124
|
if trailing and t.trailing:
|
|
@@ -133,6 +136,16 @@ class Core:
|
|
|
133
136
|
# breaks kept as written, its continuation lines not reindented.
|
|
134
137
|
return [text, BREAK_PARENT]
|
|
135
138
|
|
|
139
|
+
def hoist_leading(self, node: Node | None) -> Doc:
|
|
140
|
+
"""The comments before ``node``'s first token, printed now instead of with it."""
|
|
141
|
+
if node is None or node.verbatim:
|
|
142
|
+
return ""
|
|
143
|
+
first = next(node.tokens(), None)
|
|
144
|
+
if first is None or not first.leading or first.verbatim:
|
|
145
|
+
return ""
|
|
146
|
+
self._hoisted.add(id(first))
|
|
147
|
+
return self.leading_comments(first)
|
|
148
|
+
|
|
136
149
|
def leading_comments(self, t: Node, *, first_blank_allowed: bool = False) -> Doc:
|
|
137
150
|
"""The comments before a token, each on its own line, then the token's line."""
|
|
138
151
|
# Each comment stood on its own line: start one if code is already on this line.
|
|
@@ -486,6 +499,13 @@ class Core:
|
|
|
486
499
|
before.append(HARDLINE)
|
|
487
500
|
return indent([*before, parts])
|
|
488
501
|
|
|
502
|
+
def after_header(self, head_id: object | None) -> Doc:
|
|
503
|
+
"""What goes before an empty inline body: a space, or a line break when
|
|
504
|
+
the header group ``head_id`` spans lines, as Visual Studio formats it."""
|
|
505
|
+
if head_id is None:
|
|
506
|
+
return " "
|
|
507
|
+
return IfBreak(HARDLINE, " ", group_id=head_id)
|
|
508
|
+
|
|
489
509
|
def open_brace(self, context: str) -> Doc:
|
|
490
510
|
"""What goes before an opening brace: a line break or a space."""
|
|
491
511
|
style = self.settings.brace_style
|
|
@@ -193,7 +193,7 @@ class Declarations(Statements):
|
|
|
193
193
|
def p_file_scoped_namespace_declaration(self, node: Node) -> Doc:
|
|
194
194
|
return self.words(node.children)
|
|
195
195
|
|
|
196
|
-
def declaration_body(self, body: Node, context: str) -> Doc:
|
|
196
|
+
def declaration_body(self, body: Node, context: str, head_id: object | None = None) -> Doc:
|
|
197
197
|
if len(body.children) < 2 or body.verbatim:
|
|
198
198
|
return [" ", self.print(body)]
|
|
199
199
|
open_tok = body.children[0]
|
|
@@ -201,7 +201,7 @@ class Declarations(Statements):
|
|
|
201
201
|
items = body.children[1:-1]
|
|
202
202
|
if not items and not close_tok.leading and not open_tok.trailing:
|
|
203
203
|
if self.settings.empty_bodies == "inline":
|
|
204
|
-
return [
|
|
204
|
+
return [self.after_header(head_id), self.tok(open_tok), " ", self.tok(close_tok)]
|
|
205
205
|
return [self.open_brace(context), self.tok(open_tok), HARDLINE, self.tok(close_tok)]
|
|
206
206
|
inner: Doc = ""
|
|
207
207
|
if items:
|
|
@@ -261,14 +261,17 @@ class Declarations(Statements):
|
|
|
261
261
|
if body is None:
|
|
262
262
|
body = node.first_of("declaration_list", "enum_member_declaration_list")
|
|
263
263
|
attributes, header, constraints, tail = self.split_declaration(node, body)
|
|
264
|
-
|
|
264
|
+
comments = self.hoist_leading(header[0] if header else None)
|
|
265
|
+
head: list[Doc] = [self.header(header)]
|
|
265
266
|
if constraints:
|
|
266
|
-
|
|
267
|
+
head.append(self.constraints(constraints))
|
|
268
|
+
head_id = object()
|
|
269
|
+
parts: list[Doc] = [self.attribute_lines(attributes), comments, group(head, id=head_id)]
|
|
267
270
|
if body is not None:
|
|
268
271
|
if body.type == "enum_member_declaration_list":
|
|
269
|
-
parts.append(self.enum_body(body))
|
|
272
|
+
parts.append(self.enum_body(body, head_id))
|
|
270
273
|
else:
|
|
271
|
-
parts.append(self.declaration_body(body, "type"))
|
|
274
|
+
parts.append(self.declaration_body(body, "type", head_id))
|
|
272
275
|
parts.append(self.words(tail) if tail else "")
|
|
273
276
|
return parts
|
|
274
277
|
|
|
@@ -363,7 +366,7 @@ class Declarations(Statements):
|
|
|
363
366
|
|
|
364
367
|
# Enums ----------------------------------------------------------------
|
|
365
368
|
|
|
366
|
-
def enum_body(self, body: Node) -> Doc:
|
|
369
|
+
def enum_body(self, body: Node, head_id: object | None = None) -> Doc:
|
|
367
370
|
if len(body.children) < 2 or any(c.type in DIRECTIVE_TYPES for c in body.children):
|
|
368
371
|
return [" ", self.verbatim(body)]
|
|
369
372
|
open_tok = body.children[0]
|
|
@@ -371,7 +374,7 @@ class Declarations(Statements):
|
|
|
371
374
|
items, commas = self.items_and_commas(body)
|
|
372
375
|
if not items and not close_tok.leading:
|
|
373
376
|
if self.settings.empty_bodies == "inline":
|
|
374
|
-
return [
|
|
377
|
+
return [self.after_header(head_id), self.tok(open_tok), " ", self.tok(close_tok)]
|
|
375
378
|
return [self.open_brace("type"), self.tok(open_tok), HARDLINE, self.tok(close_tok)]
|
|
376
379
|
members = [item[0] for item in items if len(item) == 1]
|
|
377
380
|
if len(members) != len(items):
|
|
@@ -399,7 +402,7 @@ class Declarations(Statements):
|
|
|
399
402
|
return [
|
|
400
403
|
self.open_brace("type"),
|
|
401
404
|
self.tok(open_tok),
|
|
402
|
-
indent([HARDLINE, lines]),
|
|
405
|
+
indent([HARDLINE, lines]) if lines else "",
|
|
403
406
|
self.close_comments(close_tok),
|
|
404
407
|
HARDLINE,
|
|
405
408
|
self.tok(close_tok, leading=False),
|
|
@@ -452,17 +455,20 @@ class Declarations(Statements):
|
|
|
452
455
|
break
|
|
453
456
|
else:
|
|
454
457
|
header.append(child)
|
|
455
|
-
|
|
458
|
+
comments = self.hoist_leading(header[0] if header else None)
|
|
459
|
+
head: list[Doc] = [self.header(header)]
|
|
456
460
|
if ctor_init is not None:
|
|
457
|
-
|
|
461
|
+
head.append(group(indent([LINE, self.print(ctor_init)])))
|
|
458
462
|
if constraints:
|
|
459
|
-
|
|
463
|
+
head.append(self.constraints(constraints))
|
|
464
|
+
head_id = object()
|
|
465
|
+
parts: list[Doc] = [self.attribute_lines(attributes), comments, group(head, id=head_id)]
|
|
460
466
|
rest = node.children[tail_start:]
|
|
461
467
|
i = 0
|
|
462
468
|
while i < len(rest):
|
|
463
469
|
child = rest[i]
|
|
464
470
|
if child is body and child.type == "block":
|
|
465
|
-
parts.append(self.block(child, "member"))
|
|
471
|
+
parts.append(self.block(child, "member", head_id))
|
|
466
472
|
elif child.type == "arrow_expression_clause":
|
|
467
473
|
parts.append(self.expression_body(child))
|
|
468
474
|
elif child is accessors:
|
|
@@ -522,10 +528,13 @@ class Declarations(Statements):
|
|
|
522
528
|
return [" ", self.tok(open_tok), " ", join(" ", [self.print(a) for a in accessors]), " ", self.tok(close_tok)]
|
|
523
529
|
if not accessors and not close_tok.leading:
|
|
524
530
|
return [" ", self.tok(open_tok), " ", self.tok(close_tok)]
|
|
531
|
+
body: Doc = ""
|
|
532
|
+
if accessors:
|
|
533
|
+
body = indent([HARDLINE, self.print_list(accessors, lambda a, b: self.has_blank_before(b))])
|
|
525
534
|
return [
|
|
526
535
|
self.open_brace("accessor"),
|
|
527
536
|
self.tok(open_tok),
|
|
528
|
-
|
|
537
|
+
body,
|
|
529
538
|
self.close_comments(close_tok),
|
|
530
539
|
HARDLINE,
|
|
531
540
|
self.tok(close_tok, leading=False),
|
|
@@ -128,7 +128,7 @@ class _Link:
|
|
|
128
128
|
|
|
129
129
|
|
|
130
130
|
class Expressions(Core):
|
|
131
|
-
def block(self, block: Node, context: str) -> Doc: # pragma: no cover - Statements defines it
|
|
131
|
+
def block(self, block: Node, context: str, head_id: object | None = None) -> Doc: # pragma: no cover - Statements defines it
|
|
132
132
|
raise NotImplementedError
|
|
133
133
|
|
|
134
134
|
# Binary operators ---------------------------------------------------
|
|
@@ -724,6 +724,7 @@ class Expressions(Core):
|
|
|
724
724
|
body = node.child("body")
|
|
725
725
|
if arrow is None or body is None:
|
|
726
726
|
return self.generic(node)
|
|
727
|
+
comments = self.hoist_leading(node.children[0]) if body.type == "block" else ""
|
|
727
728
|
head: list[Doc] = []
|
|
728
729
|
previous: Node | None = None
|
|
729
730
|
for child in node.children:
|
|
@@ -734,7 +735,8 @@ class Expressions(Core):
|
|
|
734
735
|
head.append(self.print(child))
|
|
735
736
|
previous = child
|
|
736
737
|
if body.type == "block":
|
|
737
|
-
|
|
738
|
+
head_id = object()
|
|
739
|
+
return [comments, group([head, " ", self.tok(arrow)], id=head_id), self.block(body, "lambda", head_id)]
|
|
738
740
|
body_doc = self.print(body)
|
|
739
741
|
if self.self_breaking(body) or self.block_like(body):
|
|
740
742
|
return [head, " ", self.tok(arrow), " ", body_doc]
|
|
@@ -39,10 +39,13 @@ _EMBEDDED_PARENTS = frozenset(
|
|
|
39
39
|
class Statements(Expressions):
|
|
40
40
|
# Blocks ---------------------------------------------------------------
|
|
41
41
|
|
|
42
|
-
def block(self, block: Node, context: str) -> Doc:
|
|
43
|
-
"""A block after a header: the brace's line break or space, then the braces.
|
|
42
|
+
def block(self, block: Node, context: str, head_id: object | None = None) -> Doc:
|
|
43
|
+
"""A block after a header: the brace's line break or space, then the braces.
|
|
44
|
+
|
|
45
|
+
``head_id`` names the header's group, for headers whose empty body
|
|
46
|
+
moves to its own line when the header spans lines."""
|
|
44
47
|
if self.is_empty_block(block) and self.settings.empty_bodies == "inline":
|
|
45
|
-
return [
|
|
48
|
+
return [self.after_header(head_id), self.block_braces(block)]
|
|
46
49
|
return [self.open_brace(context), self.block_braces(block)]
|
|
47
50
|
|
|
48
51
|
def is_empty_block(self, block: Node) -> bool:
|
|
@@ -365,7 +368,9 @@ class Statements(Expressions):
|
|
|
365
368
|
block = node.children[-1] if node.children else None
|
|
366
369
|
if block is None or block.type != "block":
|
|
367
370
|
return self.generic(node)
|
|
368
|
-
|
|
371
|
+
comments = self.hoist_leading(node.children[0])
|
|
372
|
+
head_id = object()
|
|
373
|
+
return [comments, group(self.spaced(node.children[:-1]), id=head_id), self.block(block, "statement", head_id)]
|
|
369
374
|
|
|
370
375
|
def p_finally_clause(self, node: Node) -> Doc:
|
|
371
376
|
return self.p_catch_clause(node)
|
|
@@ -707,6 +707,110 @@ def test_obvious_parentheses_are_removed_and_clarifying_ones_kept() -> None:
|
|
|
707
707
|
)
|
|
708
708
|
|
|
709
709
|
|
|
710
|
+
def test_empty_body_after_a_header_that_spans_lines_gets_its_own_line() -> None:
|
|
711
|
+
# Visual Studio (IDE0055) keeps `{ }` on the header's line only when the
|
|
712
|
+
# header is one line.
|
|
713
|
+
check(
|
|
714
|
+
"""
|
|
715
|
+
// Redirects.
|
|
716
|
+
class RedirectModule : ModuleBase
|
|
717
|
+
{
|
|
718
|
+
public RedirectModule(string baseRoute, Uri redirectUrl, Func<IHttpContext, bool>? shouldRedirect, HttpStatusCode statusCode) { }
|
|
719
|
+
public RedirectModule(long alphabetical, long betamaxfactor, long gammaradiation, long deltaforce) : base(alphabetical) { }
|
|
720
|
+
public void Generic<TFirstTypeParameter, TSecondTypeParameter>(TFirstTypeParameter first, TSecondTypeParameter s) where TFirstTypeParameter : class { }
|
|
721
|
+
public RedirectModule(int a) : base() { }
|
|
722
|
+
// Short.
|
|
723
|
+
public void G<T>() where T : class { }
|
|
724
|
+
}
|
|
725
|
+
public abstract class EmptyAbstractWithAVeryLongNameIndeed<TFirstTypeParameter, TSecondTypeParameter> : Base where TFirstTypeParameter : class { }
|
|
726
|
+
""",
|
|
727
|
+
"""
|
|
728
|
+
// Redirects.
|
|
729
|
+
class RedirectModule : ModuleBase
|
|
730
|
+
{
|
|
731
|
+
public RedirectModule(
|
|
732
|
+
string baseRoute,
|
|
733
|
+
Uri redirectUrl,
|
|
734
|
+
Func<IHttpContext, bool>? shouldRedirect,
|
|
735
|
+
HttpStatusCode statusCode
|
|
736
|
+
)
|
|
737
|
+
{ }
|
|
738
|
+
|
|
739
|
+
public RedirectModule(long alphabetical, long betamaxfactor, long gammaradiation, long deltaforce)
|
|
740
|
+
: base(alphabetical)
|
|
741
|
+
{ }
|
|
742
|
+
|
|
743
|
+
public void Generic<TFirstTypeParameter, TSecondTypeParameter>(TFirstTypeParameter first, TSecondTypeParameter s)
|
|
744
|
+
where TFirstTypeParameter : class
|
|
745
|
+
{ }
|
|
746
|
+
|
|
747
|
+
public RedirectModule(int a) : base() { }
|
|
748
|
+
|
|
749
|
+
// Short.
|
|
750
|
+
public void G<T>() where T : class { }
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
public abstract class EmptyAbstractWithAVeryLongNameIndeed<TFirstTypeParameter, TSecondTypeParameter> : Base
|
|
754
|
+
where TFirstTypeParameter : class
|
|
755
|
+
{ }
|
|
756
|
+
""",
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
def test_empty_catch_and_lambda_bodies_after_a_header_that_spans_lines() -> None:
|
|
761
|
+
check(
|
|
762
|
+
"""
|
|
763
|
+
void M()
|
|
764
|
+
{
|
|
765
|
+
try { }
|
|
766
|
+
// Ignored.
|
|
767
|
+
catch (Exception error) when (IsRecoverable(error)) { }
|
|
768
|
+
Run((first, second, third) => { });
|
|
769
|
+
}
|
|
770
|
+
""",
|
|
771
|
+
"""
|
|
772
|
+
void M()
|
|
773
|
+
{
|
|
774
|
+
try { }
|
|
775
|
+
|
|
776
|
+
// Ignored.
|
|
777
|
+
catch (Exception error) when (IsRecoverable(
|
|
778
|
+
error
|
|
779
|
+
))
|
|
780
|
+
{ }
|
|
781
|
+
|
|
782
|
+
Run((
|
|
783
|
+
first,
|
|
784
|
+
second,
|
|
785
|
+
third
|
|
786
|
+
) =>
|
|
787
|
+
{ });
|
|
788
|
+
}
|
|
789
|
+
""",
|
|
790
|
+
max_line_length=30,
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
def test_accessor_list_and_enum_holding_only_a_comment() -> None:
|
|
795
|
+
check(
|
|
796
|
+
"class C { int P {\n// c\n} }\nenum E {\n// c\n}\n",
|
|
797
|
+
"""
|
|
798
|
+
class C
|
|
799
|
+
{
|
|
800
|
+
int P
|
|
801
|
+
{
|
|
802
|
+
// c
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
enum E
|
|
807
|
+
{
|
|
808
|
+
// c
|
|
809
|
+
}
|
|
810
|
+
""",
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
|
|
710
814
|
def test_auto_properties_and_empty_bodies_stay_on_one_line() -> None:
|
|
711
815
|
check(
|
|
712
816
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|