@@ -103,6 +103,10 @@ def stringify_single_line_nodes(nodes):
103103 result += " "
104104 if previous and previous .type == "=>" :
105105 result += " "
106+ if previous and previous .type == "{" :
107+ result += " "
108+ if previous and node .type == "}" :
109+ result += " "
106110 result += string
107111 previous = node
108112 return result
@@ -207,6 +211,110 @@ def stringify(node, indent, line_length):
207211 return [single_line ]
208212
209213
214+ def has_stakeholder (children ):
215+ return any (c .type == "stakeholder" for c in children )
216+
217+
218+ def stakeholder_has_comments (children ):
219+ stakeholder = next ((c for c in children if c .type == "stakeholder" ), None )
220+ if not stakeholder :
221+ return False
222+ for child in stakeholder .children :
223+ if child .type == "list" :
224+ return any (c .type == "comment" for c in child .children )
225+ return False
226+
227+
228+ def promiser_prefix (children ):
229+ """Build the promiser text (without stakeholder)."""
230+ promiser_node = next ((c for c in children if c .type == "promiser" ), None )
231+ if not promiser_node :
232+ return None
233+ return text (promiser_node )
234+
235+
236+ def promiser_line (children ):
237+ """Build the promiser prefix: promiser + optional '-> stakeholder'."""
238+ prefix = promiser_prefix (children )
239+ if not prefix :
240+ return None
241+ arrow = next ((c for c in children if c .type == "->" ), None )
242+ stakeholder = next ((c for c in children if c .type == "stakeholder" ), None )
243+ if arrow and stakeholder :
244+ prefix += " " + text (arrow ) + " " + stringify_single_line_node (stakeholder )
245+ return prefix
246+
247+
248+ def stakeholder_needs_splitting (children , indent , line_length ):
249+ """Check if the stakeholder list needs to be split across multiple lines."""
250+ if stakeholder_has_comments (children ):
251+ return True
252+ prefix = promiser_line (children )
253+ if not prefix :
254+ return False
255+ return indent + len (prefix ) > line_length
256+
257+
258+ def split_stakeholder (children , indent , has_attributes , line_length ):
259+ """Split a stakeholder list across multiple lines.
260+
261+ Returns (opening_line, element_lines, closing_str) where:
262+ - opening_line: 'promiser -> {' to print at promise indent
263+ - element_lines: pre-indented element strings
264+ - closing_str: '}' or '};' pre-indented at the appropriate level
265+ """
266+ prefix = promiser_prefix (children )
267+ assert prefix is not None
268+ opening = prefix + " -> {"
269+ stakeholder = next (c for c in children if c .type == "stakeholder" )
270+ list_node = next (c for c in stakeholder .children if c .type == "list" )
271+ middle = list_node .children [1 :- 1 ] # between { and }
272+ element_indent = indent + 4
273+ has_comments = stakeholder_has_comments (children )
274+ if has_attributes or has_comments :
275+ close_indent = indent + 2
276+ else :
277+ close_indent = indent
278+ elements = format_stakeholder_elements (middle , element_indent , line_length )
279+ return opening , elements , close_indent
280+
281+
282+ def has_trailing_comma (middle ):
283+ """Check if a list's middle nodes end with a trailing comma."""
284+ for node in reversed (middle ):
285+ if node .type == "," :
286+ return True
287+ if node .type != "comment" :
288+ return False
289+ return False
290+
291+
292+ def format_stakeholder_elements (middle , indent , line_length ):
293+ """Format the middle elements of a stakeholder list."""
294+ has_comments = any (n .type == "comment" for n in middle )
295+ if not has_comments :
296+ if has_trailing_comma (middle ):
297+ return split_generic_list (middle , indent , line_length )
298+ return maybe_split_generic_list (middle , indent , line_length )
299+ elements = []
300+ for node in middle :
301+ if node .type == "," :
302+ if elements :
303+ elements [- 1 ] = elements [- 1 ] + ","
304+ continue
305+ if node .type == "comment" :
306+ elements .append (" " * indent + text (node ))
307+ else :
308+ line = " " * indent + stringify_single_line_node (node )
309+ if len (line ) < line_length :
310+ elements .append (line )
311+ else :
312+ lines = split_generic_value (node , indent , line_length )
313+ elements .append (" " * indent + lines [0 ])
314+ elements .extend (lines [1 :])
315+ return elements
316+
317+
210318def can_single_line_promise (node , indent , line_length ):
211319 """Check if a promise node can be formatted on a single line."""
212320 if node .type != "promise" :
@@ -215,14 +323,23 @@ def can_single_line_promise(node, indent, line_length):
215323 attr_children = [c for c in children if c .type == "attribute" ]
216324 next_sib = node .next_named_sibling
217325 has_continuation = next_sib and next_sib .type == "half_promise"
218- if len (attr_children ) != 1 or has_continuation :
326+ if len (attr_children ) > 1 or has_continuation :
219327 return False
220- promiser_node = next ((c for c in children if c .type == "promiser" ), None )
221- if not promiser_node :
328+ # Promises with stakeholder + attributes are always multi-line
329+ if has_stakeholder (children ) and attr_children :
330+ return False
331+ # Stakeholders that need splitting can't be single-lined
332+ if has_stakeholder (children ) and stakeholder_needs_splitting (
333+ children , indent , line_length
334+ ):
222335 return False
223- line = (
224- text (promiser_node ) + " " + stringify_single_line_node (attr_children [0 ]) + ";"
225- )
336+ prefix = promiser_line (children )
337+ if not prefix :
338+ return False
339+ if attr_children :
340+ line = prefix + " " + stringify_single_line_node (attr_children [0 ]) + ";"
341+ else :
342+ line = prefix + ";"
226343 return indent + len (line ) <= line_length
227344
228345
@@ -287,23 +404,45 @@ def autoformat(node, fmt, line_length, macro_indent, indent=0):
287404 fmt .print_lines (lines , indent = 0 )
288405 return
289406 if node .type == "promise" :
290- # Single-line promise: if exactly 1 attribute, no half_promise continuation,
291- # not inside a class guard, and the whole line fits in line_length
407+ if can_single_line_promise (node , indent , line_length ):
408+ prefix = promiser_line (children )
409+ assert prefix is not None
410+ attr_node = next ((c for c in children if c .type == "attribute" ), None )
411+ if attr_node :
412+ line = prefix + " " + stringify_single_line_node (attr_node ) + ";"
413+ else :
414+ line = prefix + ";"
415+ fmt .print (line , indent )
416+ return
417+ # Multi-line promise with stakeholder that needs splitting
292418 attr_children = [c for c in children if c .type == "attribute" ]
293- next_sib = node .next_named_sibling
294- has_continuation = next_sib and next_sib .type == "half_promise"
295- if len (attr_children ) == 1 and not has_continuation :
296- promiser_node = next ((c for c in children if c .type == "promiser" ), None )
297- if promiser_node :
298- line = (
299- text (promiser_node )
300- + " "
301- + stringify_single_line_node (attr_children [0 ])
302- + ";"
303- )
304- if indent + len (line ) <= line_length :
305- fmt .print (line , indent )
306- return
419+ if has_stakeholder (children ) and stakeholder_needs_splitting (
420+ children , indent , line_length
421+ ):
422+ opening , elements , close_indent = split_stakeholder (
423+ children , indent , bool (attr_children ), line_length
424+ )
425+ fmt .print (opening , indent )
426+ fmt .print_lines (elements , indent = 0 )
427+ if attr_children :
428+ fmt .print ("}" , close_indent )
429+ else :
430+ fmt .print ("};" , close_indent )
431+ return
432+ for child in children :
433+ if child .type in {"promiser" , "->" , "stakeholder" }:
434+ continue
435+ autoformat (child , fmt , line_length , macro_indent , indent )
436+ return
437+ # Multi-line promise: print promiser (with stakeholder) then recurse for rest
438+ prefix = promiser_line (children )
439+ if prefix :
440+ fmt .print (prefix , indent )
441+ for child in children :
442+ if child .type in {"promiser" , "->" , "stakeholder" }:
443+ continue
444+ autoformat (child , fmt , line_length , macro_indent , indent )
445+ return
307446 if children :
308447 for child in children :
309448 # Blank line between bundle sections
0 commit comments