prettycsharp 1.1.8__tar.gz → 1.1.9__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.
Files changed (92) hide show
  1. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/CHANGELOG.md +13 -0
  2. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/DESIGN.md +11 -0
  3. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/PKG-INFO +1 -1
  4. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/__init__.py +1 -1
  5. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/printer/expressions.py +54 -6
  6. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/printer/statements.py +7 -2
  7. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_layout.py +118 -5
  8. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.gitattributes +0 -0
  9. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/codeql/codeql-config.yml +0 -0
  10. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/dependabot.yml +0 -0
  11. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/build.in +0 -0
  12. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/build.txt +0 -0
  13. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/fuzz.in +0 -0
  14. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/fuzz.txt +0 -0
  15. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/pip-audit.in +0 -0
  16. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/pip-audit.txt +0 -0
  17. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/runtime.in +0 -0
  18. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/runtime.txt +0 -0
  19. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/test.in +0 -0
  20. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/test.txt +0 -0
  21. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/zizmor.in +0 -0
  22. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/requirements/zizmor.txt +0 -0
  23. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/security/accepted.toml +0 -0
  24. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/security/clamav/Dockerfile +0 -0
  25. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/security/malware-accepted.toml +0 -0
  26. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/security/semgrep/Dockerfile +0 -0
  27. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/security/yara.json +0 -0
  28. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/security/yara_update.py +0 -0
  29. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/workflows/auto-merge.yml +0 -0
  30. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/workflows/ci.yml +0 -0
  31. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/workflows/fuzz.yml +0 -0
  32. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/workflows/malware-scan.yml +0 -0
  33. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/workflows/publish.yml +0 -0
  34. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/workflows/scorecard.yml +0 -0
  35. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/workflows/security.yml +0 -0
  36. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/workflows/update-yara-rules.yml +0 -0
  37. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.github/zizmor.yml +0 -0
  38. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/.gitignore +0 -0
  39. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/AGENTS.md +0 -0
  40. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/CLAUDE.md +0 -0
  41. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/LICENSE +0 -0
  42. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/README.md +0 -0
  43. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/SECURITY.md +0 -0
  44. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/fuzz/fuzz_formatter.py +0 -0
  45. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/pyproject.toml +0 -0
  46. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/scripts/security/malware_report.py +0 -0
  47. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/scripts/security/security_report.py +0 -0
  48. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/__main__.py +0 -0
  49. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/cli.py +0 -0
  50. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/conditional.py +0 -0
  51. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/config.py +0 -0
  52. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/doc.py +0 -0
  53. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/formatter.py +0 -0
  54. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/ignore.py +0 -0
  55. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/isolate.py +0 -0
  56. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/parsing.py +0 -0
  57. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/printer/__init__.py +0 -0
  58. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/printer/core.py +0 -0
  59. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/printer/declarations.py +0 -0
  60. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/project.py +0 -0
  61. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/py.typed +0 -0
  62. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/rewrites.py +0 -0
  63. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/settings.py +0 -0
  64. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/syntax.py +0 -0
  65. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/src/prettycsharp/verify.py +0 -0
  66. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/comment_only_accessor_list.cs +0 -0
  67. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/conditional_assignments.cs +0 -0
  68. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/declarations.cs +0 -0
  69. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/directives.cs +0 -0
  70. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/embedded_statements.cs +0 -0
  71. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/expressions.cs +0 -0
  72. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/members.cs +0 -0
  73. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/order.cs +0 -0
  74. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/queries_and_patterns.cs +0 -0
  75. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/split_by_conditionals.cs +0 -0
  76. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/statements.cs +0 -0
  77. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/fuzz_corpus/csharp/strings.cs +0 -0
  78. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/support.py +0 -0
  79. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_cli.py +0 -0
  80. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_conditional.py +0 -0
  81. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_config.py +0 -0
  82. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_doc.py +0 -0
  83. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_docs.py +0 -0
  84. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_format_around.py +0 -0
  85. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_fuzz_corpus.py +0 -0
  86. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_grammar_compatibility.py +0 -0
  87. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_ignore.py +0 -0
  88. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_parsing.py +0 -0
  89. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_project.py +0 -0
  90. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_rewrites.py +0 -0
  91. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/tests/test_settings.py +0 -0
  92. {prettycsharp-1.1.8 → prettycsharp-1.1.9}/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.9] - 2026-10-18
6
+
7
+ - With the default `binary_wrap`, multiline conditions in catch filters,
8
+ `if`, and loops start below `(` and put `)` on its own line. This includes
9
+ pattern chains, mixed Boolean conditions, and predicate calls.
10
+ - Inline collection expressions keep tight brackets around complex elements
11
+ and spreads, such as `[new HpackField(wire, "")]` and `[.. source.Select(...)]`,
12
+ avoiding Visual Studio's IDE0055 formatting diagnostic.
13
+ - A multiline collection argument no longer adds a blank line after the
14
+ call's opening parenthesis.
15
+ - Chains of negated patterns, including `is not A and not B` followed by
16
+ Boolean operators, wrap at one consistent continuation indentation level.
17
+
5
18
  ## [1.1.8] - 2026-10-18
6
19
 
7
20
  - Long `or` and `and` patterns wrap at the configured line width using
@@ -178,6 +178,12 @@ parenthesis unless all three clauses are empty: `for (; ; )`.
178
178
  | `lambda_body` | `hug`: the last argument hangs off the call line when it is a lambda with a block body or a broken member chain, or a creation with a broken initializer | `argument` |
179
179
 
180
180
  `binary_wrap` also controls line breaks in `or` and `and` patterns.
181
+ With `before_operator_paren_own_line`, broken conditions in catch
182
+ filters, `if`, `while`, and `do` loops put the expression below `(` and `)`
183
+ on its own line, including predicate calls and mixed Boolean/pattern
184
+ conditions. Pattern operators take one further continuation indent.
185
+ Negated pattern chains and trailing Boolean operators share one continuation
186
+ indent, including `value is not A and not B && condition`.
181
187
 
182
188
  ```csharp
183
189
  var invoice = invoiceService.CreateInvoice(
@@ -225,6 +231,11 @@ orders.ForEach(order =>
225
231
  | `simple_lists_inline` | `true`: lists of literals, constants, and plain names stay on one line when they fit | `false` |
226
232
  | `trailing_commas` | `remove` | `add`, `preserve` |
227
233
 
234
+ Inline collection expressions keep their brackets tight, including complex
235
+ elements: `[new Item()]`. Broken collections put the elements on new lines.
236
+ When a collection starts on an argument's own line, its opening bracket
237
+ does not add another line break.
238
+
228
239
  ```csharp
229
240
  var empty = new Order { Id = 42 };
230
241
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: prettycsharp
3
- Version: 1.1.8
3
+ Version: 1.1.9
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.8"
7
+ __version__ = "1.1.9"
8
8
 
9
9
  __all__ = [
10
10
  "CSharpSyntaxError",
@@ -7,6 +7,7 @@ from collections.abc import Sequence
7
7
  from dataclasses import dataclass, field
8
8
 
9
9
  from prettycsharp.doc import (
10
+ ENSURE_LINE,
10
11
  HARDLINE,
11
12
  LINE,
12
13
  SOFTLINE,
@@ -678,7 +679,10 @@ class Expressions(Core):
678
679
  return group([self.tok(open_tok), indent([SOFTLINE, body]), self.closing(close_tok, SOFTLINE)])
679
680
  force = self._forced_initializer([[_element_value(i[0])] for i in items if len(i) == 1])
680
681
  return group(
681
- [SOFTLINE, self.tok(open_tok), indent([LINE, join(LINE, docs)]), self.closing(close_tok, LINE)],
682
+ [
683
+ IfBreak(ENSURE_LINE, ""), self.tok(open_tok),
684
+ indent([SOFTLINE, join(LINE, docs)]), self.closing(close_tok, SOFTLINE),
685
+ ],
682
686
  should_break=force,
683
687
  )
684
688
 
@@ -870,8 +874,16 @@ class Expressions(Core):
870
874
  def p_is_expression(self, node: Node) -> Doc:
871
875
  return self.spaced(node.children)
872
876
 
873
- def p_is_pattern_expression(self, node: Node) -> Doc:
874
- return self.spaced(node.children)
877
+ def p_is_pattern_expression(self, node: Node, *, in_condition: bool = False) -> Doc:
878
+ pattern = node.child("pattern")
879
+ if not in_condition or pattern is None or pattern.verbatim or pattern.type not in (
880
+ "and_pattern", "or_pattern", "negated_pattern"
881
+ ):
882
+ return self.spaced(node.children)
883
+ return join(" ", [
884
+ self.logical_pattern(child, group_result=False) if child is pattern else self.print(child)
885
+ for child in node.children
886
+ ])
875
887
 
876
888
  def p_range_expression(self, node: Node) -> Doc:
877
889
  return self.join_tokens([(c, self.print(c)) for c in node.children])
@@ -941,13 +953,49 @@ class Expressions(Core):
941
953
  return self.spaced(node.children)
942
954
 
943
955
  def p_and_pattern(self, node: Node) -> Doc:
944
- return self.binary(node, in_condition=False)
956
+ return self.logical_pattern(node)
945
957
 
946
958
  def p_or_pattern(self, node: Node) -> Doc:
947
- return self.binary(node, in_condition=False)
959
+ return self.logical_pattern(node)
948
960
 
949
961
  def p_negated_pattern(self, node: Node) -> Doc:
950
- return self.spaced(node.children)
962
+ return self.logical_pattern(node)
963
+
964
+ def logical_pattern(self, node: Node, *, group_result: bool = True) -> Doc:
965
+ # The grammar nests `not A and not B` under successive negations,
966
+ # sometimes including trailing &&/|| expressions in constant patterns.
967
+ # Share a continuation level without moving any tokens or parentheses.
968
+ segments: list[list[Node]] = [[]]
969
+ operators: list[Node] = []
970
+ stack = [node]
971
+ while stack:
972
+ current = stack.pop()
973
+ if current.type == "constant_pattern" and len(current.children) == 1 and not current.verbatim:
974
+ value = current.children[0]
975
+ value_op = value.child("operator")
976
+ if value.type == "binary_expression" and value_op is not None and value_op.text in ("&&", "||"):
977
+ stack.append(value)
978
+ continue
979
+ op = current.child("operator")
980
+ logical = current.type in ("and_pattern", "or_pattern", "negated_pattern") or (
981
+ current.type == "binary_expression" and op is not None and op.text in ("&&", "||")
982
+ )
983
+ if logical and not current.verbatim:
984
+ stack.extend(reversed(current.children))
985
+ elif not current.named and current.text in ("and", "or", "&&", "||"):
986
+ operators.append(current)
987
+ segments.append([])
988
+ else:
989
+ segments[-1].append(current)
990
+ first = self.spaced(segments[0])
991
+ rest: list[Doc] = []
992
+ for operator, segment in zip(operators, segments[1:], strict=True):
993
+ if self.settings.binary_wrap == "after_operator":
994
+ rest.append([" ", self.tok(operator), LINE, self.spaced(segment)])
995
+ else:
996
+ rest.append([LINE, self.tok(operator), " ", self.spaced(segment)])
997
+ contents: Doc = [first, indent(rest)]
998
+ return group(contents) if group_result else contents
951
999
 
952
1000
  def p_declaration_pattern(self, node: Node) -> Doc:
953
1001
  return self.spaced(node.children)
@@ -144,13 +144,18 @@ class Statements(Expressions):
144
144
  def condition(self, open_tok: Node, condition: Node, close_tok: Node) -> Doc:
145
145
  if (
146
146
  self.settings.binary_wrap == "before_operator_paren_own_line"
147
- and condition.type == "binary_expression"
148
147
  and not condition.verbatim
149
148
  ):
149
+ if condition.type == "binary_expression":
150
+ contents = self.binary(condition, in_condition=True)
151
+ elif condition.type == "is_pattern_expression":
152
+ contents = self.p_is_pattern_expression(condition, in_condition=True)
153
+ else:
154
+ contents = self.print(condition)
150
155
  return group(
151
156
  [
152
157
  self.tok(open_tok),
153
- indent([SOFTLINE, self.binary(condition, in_condition=True)]),
158
+ indent([SOFTLINE, contents]),
154
159
  SOFTLINE,
155
160
  self.tok(close_tok),
156
161
  ]
@@ -113,8 +113,67 @@ def test_catch_filter_wraps_logical_pattern_before_expanding_property_pattern()
113
113
  'try { Run(); } catch (Exception error) when (error is ObjectDisposedException or SocketException '
114
114
  'or ArgumentException { ParamName: "socketAddress" }) { Stop(); }',
115
115
  'try\n{\n Run();\n}\n\n'
116
- 'catch (Exception error) when (error is ObjectDisposedException\n'
117
- ' or SocketException\n or ArgumentException { ParamName: "socketAddress" })\n{\n Stop();\n}\n',
116
+ 'catch (Exception error) when (\n error is ObjectDisposedException\n'
117
+ ' or SocketException\n or ArgumentException { ParamName: "socketAddress" }\n)\n{\n Stop();\n}\n',
118
+ )
119
+
120
+
121
+ def test_broken_pattern_catch_filter_puts_parentheses_on_own_lines() -> None:
122
+ check(
123
+ 'try { Run(); } catch (Exception error) when '
124
+ '(error is SocketException or ObjectDisposedException or OperationCanceledException) { }',
125
+ 'try\n{\n Run();\n}\n\ncatch (Exception error) when (\n'
126
+ ' error is SocketException\n or ObjectDisposedException\n'
127
+ ' or OperationCanceledException\n)\n{ }\n',
128
+ max_line_length=90,
129
+ )
130
+
131
+
132
+ @pytest.mark.parametrize("keyword", ["if", "while"])
133
+ def test_broken_pattern_conditions_share_catch_filter_layout(keyword: str) -> None:
134
+ check(
135
+ f'{keyword} (error is SocketException or ObjectDisposedException or OperationCanceledException) {{ Stop(); }}',
136
+ f'{keyword} (\n error is SocketException\n or ObjectDisposedException\n'
137
+ ' or OperationCanceledException\n)\n{\n Stop();\n}\n',
138
+ max_line_length=65,
139
+ )
140
+
141
+
142
+ def test_broken_pattern_do_while_condition_has_own_parenthesis_lines() -> None:
143
+ check(
144
+ 'do { Run(); } while (error is SocketException or ObjectDisposedException or OperationCanceledException);',
145
+ 'do\n{\n Run();\n}\n\nwhile (\n error is SocketException\n'
146
+ ' or ObjectDisposedException\n or OperationCanceledException\n);\n',
147
+ max_line_length=65,
148
+ )
149
+
150
+
151
+ @pytest.mark.parametrize("keyword", ["if", "while"])
152
+ def test_short_pattern_condition_stays_inline(keyword: str) -> None:
153
+ check(f'{keyword} (x is A or B) {{ Run(); }}', f'{keyword} (x is A or B)\n{{\n Run();\n}}\n')
154
+
155
+
156
+ @pytest.mark.parametrize("keyword", ["if", "while"])
157
+ def test_multiline_predicate_condition_uses_own_parenthesis_lines(keyword: str) -> None:
158
+ check(
159
+ f'{keyword} (Predicate(veryLongArgumentName, anotherLongArgumentName)) {{ Run(); }}',
160
+ f'{keyword} (\n Predicate(\n veryLongArgumentName,\n'
161
+ ' anotherLongArgumentName\n )\n)\n{\n Run();\n}\n',
162
+ max_line_length=50,
163
+ )
164
+
165
+
166
+ @pytest.mark.parametrize("wrap", ["before_operator", "after_operator"])
167
+ def test_pattern_conditions_respect_alternative_parenthesis_layout(wrap: str) -> None:
168
+ if wrap == "after_operator":
169
+ pattern = 'error is SocketException or\n ObjectDisposedException or\n OperationCanceledException'
170
+ else:
171
+ pattern = 'error is SocketException\n or ObjectDisposedException\n or OperationCanceledException'
172
+ check(
173
+ 'if (error is SocketException or ObjectDisposedException or OperationCanceledException) { Run(); }',
174
+ f'if ({pattern})\n{{\n Run();\n}}\n',
175
+ max_line_length=65,
176
+ binary_wrap=wrap,
118
177
  )
119
178
 
120
179
 
@@ -154,6 +213,16 @@ def test_short_logical_patterns_keep_grouping_and_stay_inline(pattern: str) -> N
154
213
  check(f"var ok=value is {pattern};", f"var ok = value is {pattern};\n")
155
214
 
156
215
 
216
+ def test_negated_pattern_chain_and_boolean_tail_share_one_indent() -> None:
217
+ check(
218
+ "var persistent = _status is not 400 and not 408 and not 411 and not 413 and not 414 "
219
+ "and not 500 and not 503 && !_transport.IsDraining && KeepAlive;",
220
+ "var persistent = _status is not 400\n"
221
+ " and not 408\n and not 411\n and not 413\n and not 414\n"
222
+ " and not 500\n and not 503\n && !_transport.IsDraining\n && KeepAlive;\n",
223
+ )
224
+
225
+
157
226
  def test_call_breaks_one_argument_per_line_with_paren_on_its_own_line() -> None:
158
227
  check(
159
228
  """
@@ -834,6 +903,50 @@ def test_spread_elements_are_spaced_and_ranges_are_not() -> None:
834
903
  )
835
904
 
836
905
 
906
+ @pytest.mark.parametrize("element", ['new HpackField(wire, "")', "GetField()", "wire.Length + 1", "wire"])
907
+ @pytest.mark.parametrize("simple_inline", [True, False])
908
+ def test_inline_collection_brackets_stay_tight(element: str, simple_inline: bool) -> None:
909
+ check(
910
+ f"Validate([ {element} ]);",
911
+ f"Validate([{element}]);\n",
912
+ simple_lists_inline=simple_inline,
913
+ )
914
+
915
+
916
+ def test_collection_brackets_in_awaited_send_stay_tight() -> None:
917
+ check(
918
+ 'await SendAsync([ new Http2Frame(6, 1, 0, frame.Payload) ], token).ConfigureAwait(false);',
919
+ 'await SendAsync([new Http2Frame(6, 1, 0, frame.Payload)], token).ConfigureAwait(false);\n',
920
+ )
921
+
922
+
923
+ def test_multiline_collection_argument_has_no_extra_blank_line() -> None:
924
+ check(
925
+ 'await SendHeadersAsync([new HpackField(":status", "200"), '
926
+ 'new HpackField("content-length", bytes.Length.ToString(CultureInfo.InvariantCulture))], '
927
+ 'bytes.Length == 0 || Request.Method == "HEAD", token).ConfigureAwait(false);',
928
+ 'await SendHeadersAsync(\n'
929
+ ' [\n'
930
+ ' new HpackField(":status", "200"),\n'
931
+ ' new HpackField("content-length", bytes.Length.ToString(CultureInfo.InvariantCulture))\n'
932
+ ' ],\n'
933
+ ' bytes.Length == 0 || Request.Method == "HEAD",\n'
934
+ ' token\n'
935
+ ').ConfigureAwait(false);\n',
936
+ )
937
+
938
+
939
+ def test_spread_of_call_in_multiline_arguments_keeps_brackets_tight() -> None:
940
+ check(
941
+ '_session = Session.Start([ .. _prefixes.Select(value => new ListenerPrefix(value)) ], '
942
+ '_certificates, TlsPolicy, _native, DeferContinue, Telemetry, _bodyPolicy, _dispatcher);',
943
+ '_session = Session.Start(\n'
944
+ ' [.. _prefixes.Select(value => new ListenerPrefix(value))],\n'
945
+ ' _certificates,\n TlsPolicy,\n _native,\n DeferContinue,\n'
946
+ ' Telemetry,\n _bodyPolicy,\n _dispatcher\n);\n',
947
+ )
948
+
949
+
837
950
  def test_empty_body_after_a_header_that_spans_lines_gets_its_own_line() -> None:
838
951
  # Visual Studio (IDE0055) keeps `{ }` on the header's line only when the
839
952
  # header is one line.
@@ -901,9 +1014,9 @@ def test_empty_catch_and_lambda_bodies_after_a_header_that_spans_lines() -> None
901
1014
  try { }
902
1015
 
903
1016
  // Ignored.
904
- catch (Exception error) when (IsRecoverable(
905
- error
906
- ))
1017
+ catch (Exception error) when (
1018
+ IsRecoverable(error)
1019
+ )
907
1020
  { }
908
1021
 
909
1022
  Run((
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes