prettycsharp 1.1.3__tar.gz → 1.1.5__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.3 → prettycsharp-1.1.5}/CHANGELOG.md +20 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/DESIGN.md +2 -1
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/PKG-INFO +1 -1
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/__init__.py +1 -1
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/conditional.py +40 -2
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/formatter.py +1 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/printer/core.py +40 -1
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/printer/declarations.py +24 -13
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/printer/expressions.py +19 -5
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/printer/statements.py +13 -4
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/settings.py +2 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_conditional.py +100 -2
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_layout.py +120 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_settings.py +6 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.gitattributes +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/codeql/codeql-config.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/dependabot.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/build.in +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/build.txt +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/fuzz.in +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/fuzz.txt +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/pip-audit.in +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/pip-audit.txt +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/runtime.in +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/runtime.txt +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/test.in +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/test.txt +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/zizmor.in +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/requirements/zizmor.txt +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/security/accepted.toml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/security/clamav/Dockerfile +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/security/malware-accepted.toml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/security/semgrep/Dockerfile +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/security/yara.json +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/security/yara_update.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/workflows/auto-merge.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/workflows/ci.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/workflows/fuzz.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/workflows/malware-scan.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/workflows/publish.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/workflows/scorecard.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/workflows/security.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/workflows/update-yara-rules.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.github/zizmor.yml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/.gitignore +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/AGENTS.md +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/CLAUDE.md +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/LICENSE +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/README.md +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/SECURITY.md +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/fuzz/fuzz_formatter.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/pyproject.toml +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/scripts/security/malware_report.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/scripts/security/security_report.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/__main__.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/cli.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/config.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/doc.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/ignore.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/isolate.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/parsing.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/printer/__init__.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/project.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/py.typed +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/rewrites.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/syntax.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/src/prettycsharp/verify.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/comment_only_accessor_list.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/declarations.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/directives.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/embedded_statements.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/expressions.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/members.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/order.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/queries_and_patterns.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/split_by_conditionals.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/statements.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/strings.cs +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/support.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_cli.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_config.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_doc.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_docs.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_format_around.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_fuzz_corpus.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_ignore.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_parsing.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_project.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_rewrites.py +0 -0
- {prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/test_verify.py +0 -0
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
Each release's section becomes the body of its GitHub release.
|
|
4
4
|
|
|
5
|
+
## [1.1.5] - 2026-10-18
|
|
6
|
+
|
|
7
|
+
- Nullable array types print tight, `object?[]?`, instead of
|
|
8
|
+
`object? []?`, and spread elements are spaced, `[.. items]`, as Visual
|
|
9
|
+
Studio formats them (IDE0055). Ranges such as `items[..2]` stay tight.
|
|
10
|
+
- An empty collection `[]` is never split across lines. When it does not
|
|
11
|
+
fit, the line breaks after `=>` or `=` and `[]` goes to the next line.
|
|
12
|
+
- No blank line is left directly above or below `#if`, `#elif`, `#else`
|
|
13
|
+
and `#endif`. A comment above an `#if` keeps the blank line above the
|
|
14
|
+
comment. The new setting `directive_blank_lines = "preserve"` keeps the
|
|
15
|
+
blank lines as written.
|
|
16
|
+
|
|
17
|
+
## [1.1.4] - 2026-10-17
|
|
18
|
+
|
|
19
|
+
- An empty body `{ }` after a header that spans lines goes on its own line,
|
|
20
|
+
as Visual Studio formats it. Before, `) { }` after a broken parameter
|
|
21
|
+
list, constructor initializer, `where` clause, base list, catch filter or
|
|
22
|
+
lambda parameter list was flagged by IDE0055. A header on one line keeps
|
|
23
|
+
`{ }` beside it.
|
|
24
|
+
|
|
5
25
|
## [1.1.3] - 2026-10-17
|
|
6
26
|
|
|
7
27
|
- A property's accessor list or an enum that holds only a comment no longer
|
|
@@ -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
|
|
|
@@ -134,6 +134,7 @@ while (item != null);
|
|
|
134
134
|
| `member_blank_lines` | `group_fields`: consecutive fields and auto-properties stay tight, one blank line around methods, constructors, and other members with bodies | `all`, `preserve` |
|
|
135
135
|
| `blank_lines_around_blocks` | `true`: a block statement gets a blank line before and after it, except right after `{` or right before `}` | `false` |
|
|
136
136
|
| `max_blank_lines` | `1`: author's blank lines between plain statements are kept, runs collapse to one | any integer, `0` |
|
|
137
|
+
| `directive_blank_lines` | `remove`: no blank line directly above or below `#if`, `#elif`, `#else` and `#endif` | `preserve` |
|
|
137
138
|
|
|
138
139
|
No blank line ever follows `{` or precedes `}`. A comment directly above a block moves with
|
|
139
140
|
it, so the blank line goes above the comment.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: prettycsharp
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.5
|
|
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.5"
|
|
8
8
|
|
|
9
9
|
__all__ = [
|
|
10
10
|
"CSharpSyntaxError",
|
|
@@ -369,18 +369,53 @@ def covering_sets(source: str, usable: Callable[[str], bool] = parses) -> list[f
|
|
|
369
369
|
return chosen
|
|
370
370
|
|
|
371
371
|
|
|
372
|
+
def tighten(text: str, hidden: dict[str, str]) -> str:
|
|
373
|
+
"""Drop the blank lines next to conditional directive lines behind placeholders.
|
|
374
|
+
|
|
375
|
+
A placeholder whose hidden text starts with #if, #elif, #else or #endif
|
|
376
|
+
loses the blank lines above it; one whose hidden text ends with one loses
|
|
377
|
+
the blank lines below it.
|
|
378
|
+
"""
|
|
379
|
+
lines = _split_lines(text)
|
|
380
|
+
drop: set[int] = set()
|
|
381
|
+
for number, line in enumerate(lines):
|
|
382
|
+
match = _PLACEHOLDER.match(_content(line))
|
|
383
|
+
if match is None or match.group(1) not in hidden:
|
|
384
|
+
continue
|
|
385
|
+
rows = hidden[match.group(1)].splitlines()
|
|
386
|
+
if _conditional(rows[0]):
|
|
387
|
+
above = number - 1
|
|
388
|
+
while above >= 0 and not _content(lines[above]).strip():
|
|
389
|
+
drop.add(above)
|
|
390
|
+
above -= 1
|
|
391
|
+
if _conditional(rows[-1]):
|
|
392
|
+
below = number + 1
|
|
393
|
+
while below < len(lines) and not _content(lines[below]).strip():
|
|
394
|
+
drop.add(below)
|
|
395
|
+
below += 1
|
|
396
|
+
return "".join(line for number, line in enumerate(lines) if number not in drop)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _conditional(line: str) -> bool:
|
|
400
|
+
found = _directive(line)
|
|
401
|
+
return found is not None and found[0] in ("if", "elif", "else", "endif")
|
|
402
|
+
|
|
403
|
+
|
|
372
404
|
def format_conditional(
|
|
373
405
|
source: str,
|
|
374
406
|
format_masked: Callable[[str], str],
|
|
375
407
|
same_code: Callable[[str, str], bool],
|
|
376
408
|
usable: Callable[[str], bool] = parses,
|
|
409
|
+
*,
|
|
410
|
+
tight: bool = False,
|
|
377
411
|
) -> str | None:
|
|
378
412
|
"""Format each symbol set's view of the file in turn.
|
|
379
413
|
|
|
380
414
|
Returns None when no symbol set gives text that is ``usable`` (parses,
|
|
381
415
|
by default). Raises ConditionalError when the result, seen through any
|
|
382
416
|
of the symbol sets used, is not the same code as the source
|
|
383
|
-
(``same_code`` compares two masked texts).
|
|
417
|
+
(``same_code`` compares two masked texts). With ``tight``, blank lines
|
|
418
|
+
next to conditional directives are dropped.
|
|
384
419
|
"""
|
|
385
420
|
sets = covering_sets(source, usable)
|
|
386
421
|
if not sets:
|
|
@@ -388,7 +423,10 @@ def format_conditional(
|
|
|
388
423
|
text = source
|
|
389
424
|
for symbols in sets:
|
|
390
425
|
masked = mask(text, symbols)
|
|
391
|
-
|
|
426
|
+
formatted = format_masked(masked.text)
|
|
427
|
+
if tight:
|
|
428
|
+
formatted = tighten(formatted, masked.hidden)
|
|
429
|
+
text = restore(formatted, masked.hidden)
|
|
392
430
|
for symbols in sets:
|
|
393
431
|
if not same_code(mask(source, symbols).text, mask(text, symbols).text):
|
|
394
432
|
raise BranchChanged("a branch of an #if would change in meaning")
|
|
@@ -92,6 +92,7 @@ def _format(source: str, settings: Settings, lang_version: float) -> FormatResul
|
|
|
92
92
|
functools.partial(_format_view, settings=hiding, lang_version=lang_version, around=around, kept=kept),
|
|
93
93
|
functools.partial(_same_view, around=around),
|
|
94
94
|
_usable_around if around else conditional.parses,
|
|
95
|
+
tight=settings.directive_blank_lines == "remove",
|
|
95
96
|
)
|
|
96
97
|
except conditional.BranchChanged as error:
|
|
97
98
|
raise FormattingError(str(error)) from None
|
|
@@ -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,
|
|
@@ -71,6 +72,9 @@ _MERGING_PAIRS = {
|
|
|
71
72
|
_URL = re.compile(r"\w+://")
|
|
72
73
|
_LIST_ITEM = re.compile(r"^(?:[-*+]\s|\d+[.)]\s)")
|
|
73
74
|
|
|
75
|
+
# Directives that open a conditional branch; as list items they end in #endif.
|
|
76
|
+
_CONDITIONAL_TYPES = frozenset({"preproc_if", "preproc_elif", "preproc_else"})
|
|
77
|
+
|
|
74
78
|
_INDENTING = frozenset(
|
|
75
79
|
{"declaration_list", "block", "accessor_list", "enum_member_declaration_list"}
|
|
76
80
|
)
|
|
@@ -90,6 +94,8 @@ class Core:
|
|
|
90
94
|
self.file = file
|
|
91
95
|
self.lang = lang_version
|
|
92
96
|
self._blank_stack: list[Callable[[Node, Node], bool]] = []
|
|
97
|
+
# Tokens whose leading comments were printed ahead of them.
|
|
98
|
+
self._hoisted: set[int] = set()
|
|
93
99
|
self._handlers: dict[str, Handler] = {}
|
|
94
100
|
for name in dir(self):
|
|
95
101
|
if name.startswith("p_"):
|
|
@@ -115,7 +121,7 @@ class Core:
|
|
|
115
121
|
if t.text is None:
|
|
116
122
|
return self.print(t)
|
|
117
123
|
parts: list[Doc] = []
|
|
118
|
-
if leading and t.leading:
|
|
124
|
+
if leading and t.leading and id(t) not in self._hoisted:
|
|
119
125
|
parts.append(self.leading_comments(t))
|
|
120
126
|
parts.append(self.token_text(t))
|
|
121
127
|
if trailing and t.trailing:
|
|
@@ -133,6 +139,16 @@ class Core:
|
|
|
133
139
|
# breaks kept as written, its continuation lines not reindented.
|
|
134
140
|
return [text, BREAK_PARENT]
|
|
135
141
|
|
|
142
|
+
def hoist_leading(self, node: Node | None) -> Doc:
|
|
143
|
+
"""The comments before ``node``'s first token, printed now instead of with it."""
|
|
144
|
+
if node is None or node.verbatim:
|
|
145
|
+
return ""
|
|
146
|
+
first = next(node.tokens(), None)
|
|
147
|
+
if first is None or not first.leading or first.verbatim:
|
|
148
|
+
return ""
|
|
149
|
+
self._hoisted.add(id(first))
|
|
150
|
+
return self.leading_comments(first)
|
|
151
|
+
|
|
136
152
|
def leading_comments(self, t: Node, *, first_blank_allowed: bool = False) -> Doc:
|
|
137
153
|
"""The comments before a token, each on its own line, then the token's line."""
|
|
138
154
|
# Each comment stood on its own line: start one if code is already on this line.
|
|
@@ -331,6 +347,22 @@ class Core:
|
|
|
331
347
|
return first.leading[0].newlines_before >= 2
|
|
332
348
|
return first.newlines_before >= 2
|
|
333
349
|
|
|
350
|
+
def tight_directive(self, previous: Node, item: Node) -> bool:
|
|
351
|
+
"""Whether ``directive_blank_lines`` removes the blank line between two items.
|
|
352
|
+
|
|
353
|
+
An #if item ends in #endif; a member can start with an #if around its
|
|
354
|
+
attributes. A comment above an #if keeps the blank line above it,
|
|
355
|
+
since that line is not next to the directive.
|
|
356
|
+
"""
|
|
357
|
+
if self.settings.directive_blank_lines != "remove":
|
|
358
|
+
return False
|
|
359
|
+
if previous.type in _CONDITIONAL_TYPES:
|
|
360
|
+
return True
|
|
361
|
+
starts_with_if = item.type in _CONDITIONAL_TYPES or (
|
|
362
|
+
bool(item.children) and item.children[0].type == "preproc_if_in_attribute_list"
|
|
363
|
+
)
|
|
364
|
+
return starts_with_if and not self.has_comments_before(item)
|
|
365
|
+
|
|
334
366
|
def has_comments_before(self, node: Node) -> bool:
|
|
335
367
|
first = node.first_token()
|
|
336
368
|
return first is not None and bool(first.leading)
|
|
@@ -486,6 +518,13 @@ class Core:
|
|
|
486
518
|
before.append(HARDLINE)
|
|
487
519
|
return indent([*before, parts])
|
|
488
520
|
|
|
521
|
+
def after_header(self, head_id: object | None) -> Doc:
|
|
522
|
+
"""What goes before an empty inline body: a space, or a line break when
|
|
523
|
+
the header group ``head_id`` spans lines, as Visual Studio formats it."""
|
|
524
|
+
if head_id is None:
|
|
525
|
+
return " "
|
|
526
|
+
return IfBreak(HARDLINE, " ", group_id=head_id)
|
|
527
|
+
|
|
489
528
|
def open_brace(self, context: str) -> Doc:
|
|
490
529
|
"""What goes before an opening brace: a line break or a space."""
|
|
491
530
|
style = self.settings.brace_style
|
|
@@ -71,6 +71,8 @@ class Declarations(Statements):
|
|
|
71
71
|
return parts
|
|
72
72
|
|
|
73
73
|
def top_blank(self, previous: Node, item: Node) -> bool:
|
|
74
|
+
if self.tight_directive(previous, item):
|
|
75
|
+
return False
|
|
74
76
|
if previous.type == "file_scoped_namespace_declaration":
|
|
75
77
|
return self.settings.max_blank_lines > 0
|
|
76
78
|
if previous.type in DIRECTIVE_TYPES or item.type in DIRECTIVE_TYPES:
|
|
@@ -141,7 +143,8 @@ class Declarations(Statements):
|
|
|
141
143
|
node.text is not None and node.text.startswith("#") and node.column == 0 and not node.leading
|
|
142
144
|
)
|
|
143
145
|
line: Doc = HARDLINE
|
|
144
|
-
|
|
146
|
+
# Every line placed here is next to a directive line.
|
|
147
|
+
if self.has_blank_before(node) and self.settings.directive_blank_lines != "remove":
|
|
145
148
|
line = [HARDLINE, HARDLINE]
|
|
146
149
|
return [line, TRIM_INDENT] if at_zero else line
|
|
147
150
|
|
|
@@ -193,7 +196,7 @@ class Declarations(Statements):
|
|
|
193
196
|
def p_file_scoped_namespace_declaration(self, node: Node) -> Doc:
|
|
194
197
|
return self.words(node.children)
|
|
195
198
|
|
|
196
|
-
def declaration_body(self, body: Node, context: str) -> Doc:
|
|
199
|
+
def declaration_body(self, body: Node, context: str, head_id: object | None = None) -> Doc:
|
|
197
200
|
if len(body.children) < 2 or body.verbatim:
|
|
198
201
|
return [" ", self.print(body)]
|
|
199
202
|
open_tok = body.children[0]
|
|
@@ -201,7 +204,7 @@ class Declarations(Statements):
|
|
|
201
204
|
items = body.children[1:-1]
|
|
202
205
|
if not items and not close_tok.leading and not open_tok.trailing:
|
|
203
206
|
if self.settings.empty_bodies == "inline":
|
|
204
|
-
return [
|
|
207
|
+
return [self.after_header(head_id), self.tok(open_tok), " ", self.tok(close_tok)]
|
|
205
208
|
return [self.open_brace(context), self.tok(open_tok), HARDLINE, self.tok(close_tok)]
|
|
206
209
|
inner: Doc = ""
|
|
207
210
|
if items:
|
|
@@ -222,6 +225,8 @@ class Declarations(Statements):
|
|
|
222
225
|
return self.print_list(items, self.member_blank, aligned=aligned)
|
|
223
226
|
|
|
224
227
|
def member_blank(self, previous: Node, item: Node) -> bool:
|
|
228
|
+
if self.tight_directive(previous, item):
|
|
229
|
+
return False
|
|
225
230
|
if previous.type in DIRECTIVE_TYPES or item.type in DIRECTIVE_TYPES:
|
|
226
231
|
return self.directive_blank(previous, item)
|
|
227
232
|
if self.settings.max_blank_lines == 0:
|
|
@@ -261,14 +266,17 @@ class Declarations(Statements):
|
|
|
261
266
|
if body is None:
|
|
262
267
|
body = node.first_of("declaration_list", "enum_member_declaration_list")
|
|
263
268
|
attributes, header, constraints, tail = self.split_declaration(node, body)
|
|
264
|
-
|
|
269
|
+
comments = self.hoist_leading(header[0] if header else None)
|
|
270
|
+
head: list[Doc] = [self.header(header)]
|
|
265
271
|
if constraints:
|
|
266
|
-
|
|
272
|
+
head.append(self.constraints(constraints))
|
|
273
|
+
head_id = object()
|
|
274
|
+
parts: list[Doc] = [self.attribute_lines(attributes), comments, group(head, id=head_id)]
|
|
267
275
|
if body is not None:
|
|
268
276
|
if body.type == "enum_member_declaration_list":
|
|
269
|
-
parts.append(self.enum_body(body))
|
|
277
|
+
parts.append(self.enum_body(body, head_id))
|
|
270
278
|
else:
|
|
271
|
-
parts.append(self.declaration_body(body, "type"))
|
|
279
|
+
parts.append(self.declaration_body(body, "type", head_id))
|
|
272
280
|
parts.append(self.words(tail) if tail else "")
|
|
273
281
|
return parts
|
|
274
282
|
|
|
@@ -363,7 +371,7 @@ class Declarations(Statements):
|
|
|
363
371
|
|
|
364
372
|
# Enums ----------------------------------------------------------------
|
|
365
373
|
|
|
366
|
-
def enum_body(self, body: Node) -> Doc:
|
|
374
|
+
def enum_body(self, body: Node, head_id: object | None = None) -> Doc:
|
|
367
375
|
if len(body.children) < 2 or any(c.type in DIRECTIVE_TYPES for c in body.children):
|
|
368
376
|
return [" ", self.verbatim(body)]
|
|
369
377
|
open_tok = body.children[0]
|
|
@@ -371,7 +379,7 @@ class Declarations(Statements):
|
|
|
371
379
|
items, commas = self.items_and_commas(body)
|
|
372
380
|
if not items and not close_tok.leading:
|
|
373
381
|
if self.settings.empty_bodies == "inline":
|
|
374
|
-
return [
|
|
382
|
+
return [self.after_header(head_id), self.tok(open_tok), " ", self.tok(close_tok)]
|
|
375
383
|
return [self.open_brace("type"), self.tok(open_tok), HARDLINE, self.tok(close_tok)]
|
|
376
384
|
members = [item[0] for item in items if len(item) == 1]
|
|
377
385
|
if len(members) != len(items):
|
|
@@ -452,17 +460,20 @@ class Declarations(Statements):
|
|
|
452
460
|
break
|
|
453
461
|
else:
|
|
454
462
|
header.append(child)
|
|
455
|
-
|
|
463
|
+
comments = self.hoist_leading(header[0] if header else None)
|
|
464
|
+
head: list[Doc] = [self.header(header)]
|
|
456
465
|
if ctor_init is not None:
|
|
457
|
-
|
|
466
|
+
head.append(group(indent([LINE, self.print(ctor_init)])))
|
|
458
467
|
if constraints:
|
|
459
|
-
|
|
468
|
+
head.append(self.constraints(constraints))
|
|
469
|
+
head_id = object()
|
|
470
|
+
parts: list[Doc] = [self.attribute_lines(attributes), comments, group(head, id=head_id)]
|
|
460
471
|
rest = node.children[tail_start:]
|
|
461
472
|
i = 0
|
|
462
473
|
while i < len(rest):
|
|
463
474
|
child = rest[i]
|
|
464
475
|
if child is body and child.type == "block":
|
|
465
|
-
parts.append(self.block(child, "member"))
|
|
476
|
+
parts.append(self.block(child, "member", head_id))
|
|
466
477
|
elif child.type == "arrow_expression_clause":
|
|
467
478
|
parts.append(self.expression_body(child))
|
|
468
479
|
elif child is accessors:
|
|
@@ -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 ---------------------------------------------------
|
|
@@ -277,11 +277,19 @@ class Expressions(Core):
|
|
|
277
277
|
if value.verbatim:
|
|
278
278
|
return False
|
|
279
279
|
if value.type in _BLOCK_LIKE:
|
|
280
|
-
|
|
280
|
+
# An empty `[]` is one word: it moves to the next line whole.
|
|
281
|
+
return not self._empty_collection(value)
|
|
281
282
|
if value.type in _CREATION_TYPES:
|
|
282
283
|
return value.child("initializer") is not None or value.first_of("initializer_expression") is not None
|
|
283
284
|
return False
|
|
284
285
|
|
|
286
|
+
def _empty_collection(self, node: Node) -> bool:
|
|
287
|
+
"""An empty collection expression with no comments inside: `[]`."""
|
|
288
|
+
if node.type != "collection_expression" or len(node.children) != 2:
|
|
289
|
+
return False
|
|
290
|
+
open_tok, close_tok = node.children
|
|
291
|
+
return not open_tok.trailing and not close_tok.leading
|
|
292
|
+
|
|
285
293
|
# Member chains -------------------------------------------------------
|
|
286
294
|
|
|
287
295
|
def p_invocation_expression(self, node: Node) -> Doc:
|
|
@@ -655,6 +663,8 @@ class Expressions(Core):
|
|
|
655
663
|
open_tok = node.children[0]
|
|
656
664
|
close_tok = node.children[-1]
|
|
657
665
|
items, commas = self.items_and_commas(node)
|
|
666
|
+
if self._empty_collection(node):
|
|
667
|
+
return [self.tok(open_tok), self.tok(close_tok)]
|
|
658
668
|
if not items:
|
|
659
669
|
return group([self.tok(open_tok), self.closing(close_tok, SOFTLINE)])
|
|
660
670
|
docs = self._comma_docs(items, commas, may_add=True)
|
|
@@ -724,6 +734,7 @@ class Expressions(Core):
|
|
|
724
734
|
body = node.child("body")
|
|
725
735
|
if arrow is None or body is None:
|
|
726
736
|
return self.generic(node)
|
|
737
|
+
comments = self.hoist_leading(node.children[0]) if body.type == "block" else ""
|
|
727
738
|
head: list[Doc] = []
|
|
728
739
|
previous: Node | None = None
|
|
729
740
|
for child in node.children:
|
|
@@ -734,7 +745,8 @@ class Expressions(Core):
|
|
|
734
745
|
head.append(self.print(child))
|
|
735
746
|
previous = child
|
|
736
747
|
if body.type == "block":
|
|
737
|
-
|
|
748
|
+
head_id = object()
|
|
749
|
+
return [comments, group([head, " ", self.tok(arrow)], id=head_id), self.block(body, "lambda", head_id)]
|
|
738
750
|
body_doc = self.print(body)
|
|
739
751
|
if self.self_breaking(body) or self.block_like(body):
|
|
740
752
|
return [head, " ", self.tok(arrow), " ", body_doc]
|
|
@@ -1003,7 +1015,8 @@ class Expressions(Core):
|
|
|
1003
1015
|
return self.join_tokens([(c, self.print(c)) for c in node.children])
|
|
1004
1016
|
|
|
1005
1017
|
def p_array_type(self, node: Node) -> Doc:
|
|
1006
|
-
|
|
1018
|
+
# A rank follows its element type tight, nullable or not: `object?[]`.
|
|
1019
|
+
return [self.print(c) for c in node.children]
|
|
1007
1020
|
|
|
1008
1021
|
def p_array_rank_specifier(self, node: Node) -> Doc:
|
|
1009
1022
|
parts: list[Doc] = []
|
|
@@ -1038,7 +1051,8 @@ class Expressions(Core):
|
|
|
1038
1051
|
return self.generic(node)
|
|
1039
1052
|
|
|
1040
1053
|
def p_spread_element(self, node: Node) -> Doc:
|
|
1041
|
-
|
|
1054
|
+
# Visual Studio spaces a spread, `[.. items]`, unlike a range, `items[..2]`.
|
|
1055
|
+
return self.spaced(node.children)
|
|
1042
1056
|
|
|
1043
1057
|
|
|
1044
1058
|
class _OperatorMarker(Node):
|
|
@@ -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:
|
|
@@ -73,6 +76,8 @@ class Statements(Expressions):
|
|
|
73
76
|
return self.print_list(items, self.statement_blank, aligned=self.aligned_assignments(items))
|
|
74
77
|
|
|
75
78
|
def statement_blank(self, previous: Node, item: Node) -> bool:
|
|
79
|
+
if self.tight_directive(previous, item):
|
|
80
|
+
return False
|
|
76
81
|
if previous.type in DIRECTIVE_TYPES or item.type in DIRECTIVE_TYPES:
|
|
77
82
|
return self.has_blank_before(item)
|
|
78
83
|
if self.settings.blank_lines_around_blocks and (self.is_block_statement(previous) or self.is_block_statement(item)):
|
|
@@ -318,6 +323,8 @@ class Statements(Expressions):
|
|
|
318
323
|
return [self.tok(open_tok), body, self.close_comments(close_tok), HARDLINE, self.tok(close_tok, leading=False)]
|
|
319
324
|
|
|
320
325
|
def section_blank(self, previous: Node, item: Node) -> bool:
|
|
326
|
+
if self.tight_directive(previous, item):
|
|
327
|
+
return False
|
|
321
328
|
if previous.type in DIRECTIVE_TYPES or item.type in DIRECTIVE_TYPES:
|
|
322
329
|
return self.has_blank_before(item)
|
|
323
330
|
if not self.settings.switch_case_blank_lines or self.settings.max_blank_lines == 0:
|
|
@@ -365,7 +372,9 @@ class Statements(Expressions):
|
|
|
365
372
|
block = node.children[-1] if node.children else None
|
|
366
373
|
if block is None or block.type != "block":
|
|
367
374
|
return self.generic(node)
|
|
368
|
-
|
|
375
|
+
comments = self.hoist_leading(node.children[0])
|
|
376
|
+
head_id = object()
|
|
377
|
+
return [comments, group(self.spaced(node.children[:-1]), id=head_id), self.block(block, "statement", head_id)]
|
|
369
378
|
|
|
370
379
|
def p_finally_clause(self, node: Node) -> Doc:
|
|
371
380
|
return self.p_catch_clause(node)
|
|
@@ -58,6 +58,7 @@ class Settings:
|
|
|
58
58
|
member_blank_lines: str = "group_fields"
|
|
59
59
|
blank_lines_around_blocks: bool = True
|
|
60
60
|
max_blank_lines: int = 1
|
|
61
|
+
directive_blank_lines: str = "remove"
|
|
61
62
|
|
|
62
63
|
argument_wrap: str = "one_per_line"
|
|
63
64
|
chain_break_threshold: int = 2
|
|
@@ -128,6 +129,7 @@ _CHOICES: dict[str, tuple[str, ...]] = {
|
|
|
128
129
|
"braces": ("preserve", "always", "single_line_only"),
|
|
129
130
|
"empty_bodies": ("inline", "allman"),
|
|
130
131
|
"member_blank_lines": ("group_fields", "all", "preserve"),
|
|
132
|
+
"directive_blank_lines": ("remove", "preserve"),
|
|
131
133
|
"argument_wrap": ("one_per_line", "one_per_line_close_on_last", "fill", "align"),
|
|
132
134
|
"expression_body_chain": ("own_line", "hang"),
|
|
133
135
|
"binary_wrap": ("before_operator_paren_own_line", "before_operator", "after_operator"),
|
|
@@ -87,6 +87,106 @@ def test_covering_sets_reach_every_branch() -> None:
|
|
|
87
87
|
assert frozenset({"B"}) in sets
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
def test_no_blank_lines_next_to_conditional_directives() -> None:
|
|
91
|
+
# The first parses whole; the second splits a class header, so each view
|
|
92
|
+
# is formatted on its own.
|
|
93
|
+
check(
|
|
94
|
+
"""
|
|
95
|
+
class C
|
|
96
|
+
{
|
|
97
|
+
int a;
|
|
98
|
+
|
|
99
|
+
#if X
|
|
100
|
+
|
|
101
|
+
int b;
|
|
102
|
+
|
|
103
|
+
#else
|
|
104
|
+
|
|
105
|
+
int c;
|
|
106
|
+
|
|
107
|
+
#endif
|
|
108
|
+
|
|
109
|
+
void M() { A();
|
|
110
|
+
|
|
111
|
+
#if DEBUG
|
|
112
|
+
Log();
|
|
113
|
+
#endif
|
|
114
|
+
|
|
115
|
+
B(); }
|
|
116
|
+
|
|
117
|
+
#if X
|
|
118
|
+
[A]
|
|
119
|
+
#endif
|
|
120
|
+
void N() { }
|
|
121
|
+
}
|
|
122
|
+
""",
|
|
123
|
+
"""
|
|
124
|
+
class C
|
|
125
|
+
{
|
|
126
|
+
int a;
|
|
127
|
+
#if X
|
|
128
|
+
int b;
|
|
129
|
+
#else
|
|
130
|
+
int c;
|
|
131
|
+
#endif
|
|
132
|
+
void M()
|
|
133
|
+
{
|
|
134
|
+
A();
|
|
135
|
+
#if DEBUG
|
|
136
|
+
Log();
|
|
137
|
+
#endif
|
|
138
|
+
B();
|
|
139
|
+
}
|
|
140
|
+
#if X
|
|
141
|
+
[A]
|
|
142
|
+
#endif
|
|
143
|
+
void N() { }
|
|
144
|
+
}
|
|
145
|
+
""",
|
|
146
|
+
)
|
|
147
|
+
check(
|
|
148
|
+
"""
|
|
149
|
+
#if NET
|
|
150
|
+
|
|
151
|
+
class A : B
|
|
152
|
+
{
|
|
153
|
+
|
|
154
|
+
#else
|
|
155
|
+
|
|
156
|
+
class A
|
|
157
|
+
{
|
|
158
|
+
|
|
159
|
+
#endif
|
|
160
|
+
|
|
161
|
+
int a;
|
|
162
|
+
|
|
163
|
+
#if X
|
|
164
|
+
|
|
165
|
+
int b;
|
|
166
|
+
|
|
167
|
+
#endif
|
|
168
|
+
|
|
169
|
+
void M() { }
|
|
170
|
+
}
|
|
171
|
+
""",
|
|
172
|
+
"""
|
|
173
|
+
#if NET
|
|
174
|
+
class A : B
|
|
175
|
+
{
|
|
176
|
+
#else
|
|
177
|
+
class A
|
|
178
|
+
{
|
|
179
|
+
#endif
|
|
180
|
+
int a;
|
|
181
|
+
#if X
|
|
182
|
+
int b;
|
|
183
|
+
#endif
|
|
184
|
+
void M() { }
|
|
185
|
+
}
|
|
186
|
+
""",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
90
190
|
def test_split_class_header_formats_every_branch() -> None:
|
|
91
191
|
check(
|
|
92
192
|
"""
|
|
@@ -120,7 +220,6 @@ def test_split_class_header_formats_every_branch() -> None:
|
|
|
120
220
|
#endif
|
|
121
221
|
{
|
|
122
222
|
public static int Size = 3;
|
|
123
|
-
|
|
124
223
|
#if NET10_0_OR_GREATER
|
|
125
224
|
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
|
126
225
|
#else
|
|
@@ -194,7 +293,6 @@ def test_usings_do_not_move_across_directives() -> None:
|
|
|
194
293
|
#endif
|
|
195
294
|
using Beta;
|
|
196
295
|
using Gamma;
|
|
197
|
-
|
|
198
296
|
#if NET
|
|
199
297
|
class C : I
|
|
200
298
|
#else
|
|
@@ -707,6 +707,126 @@ def test_obvious_parentheses_are_removed_and_clarifying_ones_kept() -> None:
|
|
|
707
707
|
)
|
|
708
708
|
|
|
709
709
|
|
|
710
|
+
def test_nullable_array_types_stay_tight() -> None:
|
|
711
|
+
source = (
|
|
712
|
+
"void M(params object?[]? values) { }\n"
|
|
713
|
+
"\n"
|
|
714
|
+
"int?[] a = new int?[3];\n"
|
|
715
|
+
"List<string?[]> b = (string?[][])o;\n"
|
|
716
|
+
"var c = x is int?[] d;\n"
|
|
717
|
+
"var e = f ? [1] : [2];\n"
|
|
718
|
+
"var g = h?[i];\n"
|
|
719
|
+
)
|
|
720
|
+
assert fmt(source) == source
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
def test_empty_collection_moves_to_the_next_line_whole() -> None:
|
|
724
|
+
check(
|
|
725
|
+
"class C { IEnumerable<Entry> Entries(string path, IMimeTypeProvider provider) => []; int[] xs = []; }\n",
|
|
726
|
+
"""
|
|
727
|
+
class C
|
|
728
|
+
{
|
|
729
|
+
IEnumerable<Entry> Entries(string path, IMimeTypeProvider provider) =>
|
|
730
|
+
[];
|
|
731
|
+
|
|
732
|
+
int[] xs = [];
|
|
733
|
+
}
|
|
734
|
+
""",
|
|
735
|
+
max_line_length=74,
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def test_spread_elements_are_spaced_and_ranges_are_not() -> None:
|
|
740
|
+
assert fmt("x = [..entries, 1, ..more];\n") == "x = [.. entries, 1, .. more];\n"
|
|
741
|
+
assert fmt("var r = entries[1..] + entries[..2] + entries[..];\n") == (
|
|
742
|
+
"var r = entries[1..] + entries[..2] + entries[..];\n"
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def test_empty_body_after_a_header_that_spans_lines_gets_its_own_line() -> None:
|
|
747
|
+
# Visual Studio (IDE0055) keeps `{ }` on the header's line only when the
|
|
748
|
+
# header is one line.
|
|
749
|
+
check(
|
|
750
|
+
"""
|
|
751
|
+
// Redirects.
|
|
752
|
+
class RedirectModule : ModuleBase
|
|
753
|
+
{
|
|
754
|
+
public RedirectModule(string baseRoute, Uri redirectUrl, Func<IHttpContext, bool>? shouldRedirect, HttpStatusCode statusCode) { }
|
|
755
|
+
public RedirectModule(long alphabetical, long betamaxfactor, long gammaradiation, long deltaforce) : base(alphabetical) { }
|
|
756
|
+
public void Generic<TFirstTypeParameter, TSecondTypeParameter>(TFirstTypeParameter first, TSecondTypeParameter s) where TFirstTypeParameter : class { }
|
|
757
|
+
public RedirectModule(int a) : base() { }
|
|
758
|
+
// Short.
|
|
759
|
+
public void G<T>() where T : class { }
|
|
760
|
+
}
|
|
761
|
+
public abstract class EmptyAbstractWithAVeryLongNameIndeed<TFirstTypeParameter, TSecondTypeParameter> : Base where TFirstTypeParameter : class { }
|
|
762
|
+
""",
|
|
763
|
+
"""
|
|
764
|
+
// Redirects.
|
|
765
|
+
class RedirectModule : ModuleBase
|
|
766
|
+
{
|
|
767
|
+
public RedirectModule(
|
|
768
|
+
string baseRoute,
|
|
769
|
+
Uri redirectUrl,
|
|
770
|
+
Func<IHttpContext, bool>? shouldRedirect,
|
|
771
|
+
HttpStatusCode statusCode
|
|
772
|
+
)
|
|
773
|
+
{ }
|
|
774
|
+
|
|
775
|
+
public RedirectModule(long alphabetical, long betamaxfactor, long gammaradiation, long deltaforce)
|
|
776
|
+
: base(alphabetical)
|
|
777
|
+
{ }
|
|
778
|
+
|
|
779
|
+
public void Generic<TFirstTypeParameter, TSecondTypeParameter>(TFirstTypeParameter first, TSecondTypeParameter s)
|
|
780
|
+
where TFirstTypeParameter : class
|
|
781
|
+
{ }
|
|
782
|
+
|
|
783
|
+
public RedirectModule(int a) : base() { }
|
|
784
|
+
|
|
785
|
+
// Short.
|
|
786
|
+
public void G<T>() where T : class { }
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
public abstract class EmptyAbstractWithAVeryLongNameIndeed<TFirstTypeParameter, TSecondTypeParameter> : Base
|
|
790
|
+
where TFirstTypeParameter : class
|
|
791
|
+
{ }
|
|
792
|
+
""",
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
|
|
796
|
+
def test_empty_catch_and_lambda_bodies_after_a_header_that_spans_lines() -> None:
|
|
797
|
+
check(
|
|
798
|
+
"""
|
|
799
|
+
void M()
|
|
800
|
+
{
|
|
801
|
+
try { }
|
|
802
|
+
// Ignored.
|
|
803
|
+
catch (Exception error) when (IsRecoverable(error)) { }
|
|
804
|
+
Run((first, second, third) => { });
|
|
805
|
+
}
|
|
806
|
+
""",
|
|
807
|
+
"""
|
|
808
|
+
void M()
|
|
809
|
+
{
|
|
810
|
+
try { }
|
|
811
|
+
|
|
812
|
+
// Ignored.
|
|
813
|
+
catch (Exception error) when (IsRecoverable(
|
|
814
|
+
error
|
|
815
|
+
))
|
|
816
|
+
{ }
|
|
817
|
+
|
|
818
|
+
Run((
|
|
819
|
+
first,
|
|
820
|
+
second,
|
|
821
|
+
third
|
|
822
|
+
) =>
|
|
823
|
+
{ });
|
|
824
|
+
}
|
|
825
|
+
""",
|
|
826
|
+
max_line_length=30,
|
|
827
|
+
)
|
|
828
|
+
|
|
829
|
+
|
|
710
830
|
def test_accessor_list_and_enum_holding_only_a_comment() -> None:
|
|
711
831
|
check(
|
|
712
832
|
"class C { int P {\n// c\n} }\nenum E {\n// c\n}\n",
|
|
@@ -315,6 +315,12 @@ def test_comment_space_and_wrap_off() -> None:
|
|
|
315
315
|
)
|
|
316
316
|
|
|
317
317
|
|
|
318
|
+
def test_directive_blank_lines_preserve() -> None:
|
|
319
|
+
source = "class C\n{\n int a;\n\n#if X\n\n int b;\n\n#endif\n\n int c;\n}\n"
|
|
320
|
+
assert fmt(source, directive_blank_lines="preserve") == source
|
|
321
|
+
assert fmt(source) == "class C\n{\n int a;\n#if X\n int b;\n#endif\n int c;\n}\n"
|
|
322
|
+
|
|
323
|
+
|
|
318
324
|
def test_max_blank_lines_zero() -> None:
|
|
319
325
|
assert fmt("void M() { A();\n\n B(); }\n", max_blank_lines=0) == "void M()\n{\n A();\n B();\n}\n"
|
|
320
326
|
|
|
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
|
{prettycsharp-1.1.3 → prettycsharp-1.1.5}/tests/fuzz_corpus/csharp/comment_only_accessor_list.cs
RENAMED
|
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
|