@@ -24,7 +24,7 @@ BACKUP_SUFFIX = '.orig'
2424
2525# Match a %{ on its own line then capture inner until a %} on its own line.
2626BLOCK_RE = re .compile (
27- r'(^[ \t ]*%[\s]*\{\s*\ n)(.*?)(\n[ \t ]*%\}[ \t ]*$)' ,
27+ r'(^[\s ]*%\{ [\s]*\n)(.*?)(\n[\s ]*%\}[\s ]*$)' ,
2828 re .DOTALL | re .MULTILINE
2929)
3030
@@ -38,26 +38,22 @@ def find_files(root: Path) -> List[Path]:
3838 return files
3939
4040
41- def _indent_middle_lines (core : str , indent_str : str , indent_blank_lines : bool ) -> str :
41+ def _indent_lines (core : str , indent_str : str , indent_blank_lines : bool ) -> str :
4242 """
43- Add `indent_str` to all lines of `core` except the first and last lines.
44- `core` must not have leading/trailing newlines (strip them before calling).
45- If there are fewer than 3 lines, there are no 'middle' lines to indent.
43+ Add `indent_str` to all lines of `core`. `core` may contain trailing newlines;
44+ those are preserved. We operate per-line using splitlines(True).
4645 """
4746 if not indent_str :
4847 return core
4948
50- lines = core .splitlines ()
51- if len (lines ) < 3 :
52- return core
49+ lines = core .splitlines (True ) # keep line endings
5350
54- for i in range (1 , len (lines ) - 1 ):
51+ for i in range (0 , len (lines )):
5552 if not indent_blank_lines and lines [i ].strip () == '' :
5653 continue
5754 lines [i ] = indent_str + lines [i ]
5855
59- return "\n " .join (lines )
60-
56+ return '' .join (lines )
6157
6258
6359def run_clang_format_file_style (code : str , clang_path : str , assume_filename : Path ) -> str :
@@ -75,6 +71,45 @@ def run_clang_format_file_style(code: str, clang_path: str, assume_filename: Pat
7571 return out
7672
7773
74+ def _format_preserving_percent_lines (inner : str , clang_path : str , style_file : Path ) -> str :
75+ """
76+ Format inner text with clang-format but skip lines that start with '%'.
77+ Keeps original line endings.
78+ """
79+ lines = inner .splitlines (True ) # preserve endings
80+ out_parts = []
81+ buffer = []
82+
83+ def flush_buffer ():
84+ if not buffer :
85+ return
86+ chunk = '' .join (buffer )
87+ # strip only a single trailing newline so clang-format output is normalized
88+ if chunk .endswith ('\n ' ):
89+ chunk_in = chunk [:- 1 ]
90+ trailing_nl = '\n '
91+ else :
92+ chunk_in = chunk
93+ trailing_nl = ''
94+ formatted = run_clang_format_file_style (chunk_in , clang_path = clang_path , assume_filename = style_file )
95+ # clang-format usually emits a trailing newline; strip one to maintain control
96+ if formatted .endswith ('\n ' ):
97+ formatted = formatted [:- 1 ]
98+ out_parts .append (formatted + trailing_nl )
99+ buffer .clear ()
100+
101+ for L in lines :
102+ if L .lstrip ().startswith ('%' ):
103+ # flush any accumulated non-% lines, then append this % line unchanged
104+ flush_buffer ()
105+ out_parts .append (L .lstrip ())
106+ else :
107+ buffer .append (L )
108+
109+ flush_buffer ()
110+ return '' .join (out_parts )
111+
112+
78113def process_file (path : Path , clang_path : str , style_file : Path , check_only : bool = False ,
79114 ) -> Tuple [bool , str ]:
80115 """
@@ -89,24 +124,29 @@ def process_file(path: Path, clang_path: str, style_file: Path, check_only: bool
89124 inner = m .group (2 )
90125 trailing = m .group (3 )
91126
127+ # We'll feed clang-format only the non-'%' lines, preserving '%' lines.
92128 try :
93- formatted_inner = run_clang_format_file_style (
129+ formatted_inner = _format_preserving_percent_lines (
94130 inner ,
95131 clang_path = clang_path ,
96- assume_filename = style_file # force clang-format to use the script's style file
132+ style_file = style_file
97133 )
98134 except Exception as e :
99135 raise RuntimeError (f'Formatting failed for { path } : { e } ' )
100136
101- # Strip only outer newlines; keep content as-is for first/last lines
102- core = formatted_inner .strip ('\n ' )
137+ # Preserve exact content lines from clang-format, but remove a single
138+ # trailing newline so we can control delimiter placement below.
139+ if formatted_inner .endswith ('\n ' ):
140+ formatted_core = formatted_inner [:- 1 ]
141+ else :
142+ formatted_core = formatted_inner
103143
104- # Apply variable indentation to middle lines only
105- core = _indent_middle_lines ( core , indent_str = " " , indent_blank_lines = True )
144+ # Apply variable indentation (preserves per-line endings)
145+ core = _indent_lines ( formatted_core , indent_str = " " , indent_blank_lines = False )
106146
107147 # If clang-format produced nothing meaningful, keep empty
108148 if core == '' :
109- result = leading + " \n " + trailing .lstrip ("\n " )
149+ result = leading + trailing .lstrip ("\n " )
110150 else :
111151 # Preserve delimiters on their own lines and ensure a newline before trailing
112152 result = leading + core + "\n " + trailing .lstrip ("\n " )
@@ -211,4 +251,5 @@ def main():
211251 print ('No changes necessary.' )
212252
213253if __name__ == '__main__' :
214- main ()
254+ main ()
255+
0 commit comments