@@ -49,7 +49,18 @@ def update_previous(self, node):
4949 return tmp
5050
5151
52- def stringify_children_from_strings (parts ):
52+ def stringify_parameter_list (parts ):
53+ """Join pre-extracted string tokens into a formatted parameter list.
54+
55+ Used when formatting bundle/body headers. Comments are
56+ stripped from the parameter_list node before this function is called,
57+ so `parts` contains only the structural tokens: "(", identifiers, ","
58+ separators, and ")". The function removes any trailing comma before
59+ ")", then joins the tokens with appropriate spacing (space after each
60+ comma, no space after "(" or before ")").
61+
62+ Example: ["(", "a", ",", "b", ",", ")"] -> "(a, b)"
63+ """
5364 # Remove trailing comma before closing paren
5465 cleaned = []
5566 for i , part in enumerate (parts ):
@@ -68,34 +79,47 @@ def stringify_children_from_strings(parts):
6879 return result
6980
7081
71- def stringify_children (children ):
82+ def stringify_single_line_nodes (nodes ):
83+ """Join a list of tree-sitter nodes into a single-line string.
84+
85+ Operates on the direct child nodes of a CFEngine syntax construct
86+ (e.g. a list, call, or attribute). Each child is recursively
87+ flattened via stringify_single_line_node(). Spacing rules:
88+ - A space is inserted after each "," separator.
89+ - A space is inserted before and after "=>" (fat arrow).
90+ - No extra space otherwise (e.g. no space after "(" or before ")").
91+
92+ Used by stringify_single_line_node() to recursively flatten any node with
93+ children, and by maybe_split_generic_list() to attempt a single-line
94+ rendering before falling back to multi-line splitting.
95+ """
7296 result = ""
7397 previous = None
74- for child in children :
75- string = stringify_single_line ( child )
98+ for node in nodes :
99+ string = stringify_single_line_node ( node )
76100 if previous and previous .type == "," :
77101 result += " "
78- if previous and child .type == "=>" :
102+ if previous and node .type == "=>" :
79103 result += " "
80104 if previous and previous .type == "=>" :
81105 result += " "
82106 result += string
83- previous = child
107+ previous = node
84108 return result
85109
86110
87- def stringify_single_line (node ):
111+ def stringify_single_line_node (node ):
88112 if not node .children :
89113 return text (node )
90- return stringify_children (node .children )
114+ return stringify_single_line_nodes (node .children )
91115
92116
93117def split_generic_value (node , indent , line_length ):
94118 if node .type == "call" :
95119 return split_rval_call (node , indent , line_length )
96120 if node .type == "list" :
97121 return split_rval_list (node , indent , line_length )
98- return [stringify_single_line (node )]
122+ return [stringify_single_line_node (node )]
99123
100124
101125def split_generic_list (middle , indent , line_length ):
@@ -104,7 +128,7 @@ def split_generic_list(middle, indent, line_length):
104128 if elements and element .type == "," :
105129 elements [- 1 ] = elements [- 1 ] + ","
106130 continue
107- line = " " * indent + stringify_single_line (element )
131+ line = " " * indent + stringify_single_line_node (element )
108132 if len (line ) < line_length :
109133 elements .append (line )
110134 else :
@@ -115,7 +139,7 @@ def split_generic_list(middle, indent, line_length):
115139
116140
117141def maybe_split_generic_list (nodes , indent , line_length ):
118- string = " " * indent + stringify_children (nodes )
142+ string = " " * indent + stringify_single_line_nodes (nodes )
119143 if len (string ) < line_length :
120144 return [string ]
121145 return split_generic_list (nodes , indent , line_length )
@@ -147,11 +171,11 @@ def split_rval(node, indent, line_length):
147171 return split_rval_list (node , indent , line_length )
148172 if node .type == "call" :
149173 return split_rval_call (node , indent , line_length )
150- return [stringify_single_line (node )]
174+ return [stringify_single_line_node (node )]
151175
152176
153177def maybe_split_rval (node , indent , offset , line_length ):
154- line = stringify_single_line (node )
178+ line = stringify_single_line_node (node )
155179 if len (line ) + offset < line_length :
156180 return [line ]
157181 return split_rval (node , indent , line_length )
@@ -169,11 +193,11 @@ def attempt_split_attribute(node, indent, line_length):
169193 lines = maybe_split_rval (rval , indent , offset , line_length )
170194 lines [0 ] = prefix + lines [0 ]
171195 return lines
172- return [" " * indent + stringify_single_line (node )]
196+ return [" " * indent + stringify_single_line_node (node )]
173197
174198
175199def stringify (node , indent , line_length ):
176- single_line = " " * indent + stringify_single_line (node )
200+ single_line = " " * indent + stringify_single_line_node (node )
177201 # Reserve 1 char for trailing ; or , after attributes
178202 effective_length = line_length - 1 if node .type == "attribute" else line_length
179203 if len (single_line ) < effective_length :
@@ -209,9 +233,7 @@ def autoformat(node, fmt, line_length, macro_indent, indent=0):
209233 else :
210234 parts .append (text (p ))
211235 # Append directly to previous part (no space before parens)
212- header_parts [- 1 ] = header_parts [- 1 ] + stringify_children_from_strings (
213- parts
214- )
236+ header_parts [- 1 ] = header_parts [- 1 ] + stringify_parameter_list (parts )
215237 else :
216238 header_parts .append (text (x ))
217239 line = " " .join (header_parts )
@@ -220,7 +242,15 @@ def autoformat(node, fmt, line_length, macro_indent, indent=0):
220242 if not (prev_sib and prev_sib .type == "comment" ):
221243 fmt .print ("" , 0 )
222244 fmt .print (line , 0 )
223- for comment in header_comments :
245+ for i , comment in enumerate (header_comments ):
246+ if comment .strip () == "#" :
247+ prev_is_comment = i > 0 and header_comments [i - 1 ].strip () != "#"
248+ next_is_comment = (
249+ i + 1 < len (header_comments )
250+ and header_comments [i + 1 ].strip () != "#"
251+ )
252+ if not (prev_is_comment and next_is_comment ):
253+ continue
224254 fmt .print (comment , 0 )
225255 children = node .children [- 1 ].children
226256 if node .type in [
@@ -239,26 +269,37 @@ def autoformat(node, fmt, line_length, macro_indent, indent=0):
239269 return
240270 if node .type == "promise" :
241271 # Single-line promise: if exactly 1 attribute, no half_promise continuation,
242- # and the whole line fits in line_length
272+ # not inside a class guard, and the whole line fits in line_length
243273 attr_children = [c for c in children if c .type == "attribute" ]
244274 next_sib = node .next_named_sibling
245275 has_continuation = next_sib and next_sib .type == "half_promise"
246- if len (attr_children ) == 1 and not has_continuation :
276+ parent = node .parent
277+ in_class_guard = parent and parent .type in [
278+ "class_guarded_promises" ,
279+ "class_guarded_body_attributes" ,
280+ "class_guarded_promise_block_attributes" ,
281+ ]
282+ if len (attr_children ) == 1 and not has_continuation and not in_class_guard :
247283 promiser_node = next ((c for c in children if c .type == "promiser" ), None )
248284 if promiser_node :
249285 line = (
250286 text (promiser_node )
251287 + " "
252- + stringify_single_line (attr_children [0 ])
288+ + stringify_single_line_node (attr_children [0 ])
253289 + ";"
254290 )
255291 if indent + len (line ) <= line_length :
256292 fmt .print (line , indent )
257293 return
258294 if children :
259295 for child in children :
296+ # Blank line between bundle sections
297+ if child .type == "bundle_section" :
298+ prev = child .prev_named_sibling
299+ if prev and prev .type == "bundle_section" :
300+ fmt .print ("" , 0 )
260301 # Blank line between promises in a section
261- if child .type == "promise" :
302+ elif child .type == "promise" :
262303 prev = child .prev_named_sibling
263304 if prev and prev .type in ["promise" , "half_promise" ]:
264305 fmt .print ("" , 0 )
@@ -271,6 +312,7 @@ def autoformat(node, fmt, line_length, macro_indent, indent=0):
271312 if prev and prev .type in [
272313 "promise" ,
273314 "half_promise" ,
315+ "class_guarded_promises" ,
274316 ]:
275317 fmt .print ("" , 0 )
276318 elif child .type == "comment" :
@@ -288,6 +330,11 @@ def autoformat(node, fmt, line_length, macro_indent, indent=0):
288330 fmt .print_same_line (node )
289331 return
290332 if node .type == "comment" :
333+ if text (node ).strip () == "#" :
334+ prev = node .prev_named_sibling
335+ nxt = node .next_named_sibling
336+ if not (prev and prev .type == "comment" and nxt and nxt .type == "comment" ):
337+ return
291338 comment_indent = indent
292339 next_sib = node .next_named_sibling
293340 while next_sib and next_sib .type == "comment" :
0 commit comments