1414//! rules have too much performance overhead when applied through Topiary.
1515use std:: { collections:: VecDeque , io:: BufWriter } ;
1616
17- use regex:: { Regex , RegexBuilder } ;
17+ use regex:: { Regex , RegexBuilder , Replacer } ;
1818use topiary_core:: { Language , Operation , TopiaryQuery , formatter_tree} ;
1919use tree_sitter:: { Parser , Point , Query , QueryCursor , StreamingIterator , Tree } ;
2020
@@ -165,15 +165,30 @@ impl Formatter {
165165 // - consists out of alphanumeric characters
166166 // - consists out of any characters (except new lines) between double quotes
167167 // - must contain at least one new line character between `extends_name` and optional doc comment
168- // - may contain doc string that starts with `##` and ends with new line character
168+ // - may contain multiple doc comment lines that starts with `##` and ends with a new line character
169169 let re = RegexBuilder :: new (
170- r#"(?P<extends_line>^extends )(?P<extends_name>([a-zA-Z0-9]+|".*?"))\n+(^( ?P<doc>##.*?)$ )?"# ,
170+ r#"(?P<extends_line>^extends )(?P<extends_name>([a-zA-Z0-9]+|".*?"))\n+(?P<doc>(?:^ ##.*\n)*)(?P<EOF>\z )?"# ,
171171 )
172172 . multi_line ( true )
173173 . build ( )
174174 . expect ( "regex should compile" ) ;
175175
176- self . regex_replace_all_outside_strings ( re, "$extends_line$extends_name\n $doc\n " ) ;
176+ self . regex_replace_all_outside_strings ( re, |caps : & regex:: Captures | {
177+ let extends_line = caps. name ( "extends_line" ) . unwrap ( ) . as_str ( ) ;
178+ let extends_name = caps. name ( "extends_name" ) . unwrap ( ) . as_str ( ) ;
179+ let doc = caps
180+ . name ( "doc" )
181+ . map ( |m| m. as_str ( ) )
182+ . unwrap_or_default ( )
183+ . trim_end ( ) ; // remove last new line from doc comment because we add a new line manually
184+ // insert new line only if we are not at the end of file
185+ let blank_new_line = if caps. name ( "EOF" ) . is_some ( ) { "" } else { "\n " } ;
186+
187+ format ! (
188+ "{}{}\n {}{}" ,
189+ extends_line, extends_name, doc, blank_new_line
190+ )
191+ } ) ;
177192
178193 self
179194 }
@@ -237,7 +252,7 @@ impl Formatter {
237252 /// outside of strings (simple or multiline).
238253 /// Use this to make post-processing changes needed for formatting but that
239254 /// shouldn't affect strings in the source code.
240- fn regex_replace_all_outside_strings ( & mut self , re : Regex , rep : & str ) {
255+ fn regex_replace_all_outside_strings < R : Replacer > ( & mut self , re : Regex , mut rep : R ) {
241256 let mut iter = re. captures_iter ( & self . content ) . peekable ( ) ;
242257 if iter. peek ( ) . is_none ( ) {
243258 return ;
@@ -264,7 +279,7 @@ impl Formatter {
264279 }
265280
266281 let mut replacement = String :: new ( ) ;
267- capture . expand ( rep , & mut replacement) ;
282+ rep . replace_append ( & capture , & mut replacement) ;
268283
269284 let new_end_byte = start_byte + replacement. len ( ) ;
270285
0 commit comments