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
@@ -126,14 +126,15 @@ impl Formatter {
126126 /// pre-applying rules that could be performance-intensive through topiary.
127127 #[ inline( always) ]
128128 fn preprocess ( & mut self ) -> & mut Self {
129- self . remove_newlines_after_extends_statement ( )
129+ self
130130 }
131131
132132 /// This function runs over the content after going through topiary. We use it
133133 /// to clean up/balance out the output.
134134 #[ inline( always) ]
135135 fn postprocess ( & mut self ) -> & mut Self {
136- self . fix_dangling_semicolons ( )
136+ self . add_newlines_after_extends_statement ( )
137+ . fix_dangling_semicolons ( )
137138 . fix_dangling_commas ( )
138139 . remove_trailing_commas_from_preload ( )
139140 . postprocess_tree_sitter ( )
@@ -154,23 +155,40 @@ impl Formatter {
154155 Ok ( self . content )
155156 }
156157
157- /// This function removes additional new line characters after `extends_statement`.
158+ /// This function adds additional new line characters after `extends_statement`.
158159 #[ inline( always) ]
159- fn remove_newlines_after_extends_statement ( & mut self ) -> & mut Self {
160+ fn add_newlines_after_extends_statement ( & mut self ) -> & mut Self {
160161 // This regex matches substrings which:
161- // - must NOT contain "#" or "\n" characters between new line and "extends" keyword
162- // - must end with at least one new line character
162+ // - must start wtih "extends" keyword
163163 // - must contain `extends_name` character sequence that satisfies one of the following conditions:
164164 // - consists out of alphanumeric characters
165165 // - consists out of any characters (except new lines) between double quotes
166+ // - must contain at least one new line character between `extends_name` and optional doc comment
167+ // - may contain multiple doc comment lines that starts with `##` and ends with a new line character
166168 let re = RegexBuilder :: new (
167- r#"(?P<extends_line>^[^#\n]* extends )(?P<extends_name>([a-zA-Z0-9]+|".*?"))\n(\n*) "# ,
169+ r#"(?P<extends_line>^extends )(?P<extends_name>([a-zA-Z0-9]+|".*?"))\n+(?P<doc>(?:^##.*\n)*)(?P<EOF>\z)? "# ,
168170 )
169171 . multi_line ( true )
170172 . build ( )
171173 . expect ( "regex should compile" ) ;
172174
173- self . regex_replace_all_outside_strings ( re, "$extends_line$extends_name\n " ) ;
175+ self . regex_replace_all_outside_strings ( re, |caps : & regex:: Captures | {
176+ let extends_line = caps. name ( "extends_line" ) . unwrap ( ) . as_str ( ) ;
177+ let extends_name = caps. name ( "extends_name" ) . unwrap ( ) . as_str ( ) ;
178+ let doc = caps
179+ . name ( "doc" )
180+ . map ( |m| m. as_str ( ) )
181+ . unwrap_or_default ( )
182+ . trim_end ( ) ; // remove last new line from doc comment because we add a new line manually
183+ // insert new line only if we are not at the end of file
184+ let blank_new_line = if caps. name ( "EOF" ) . is_some ( ) { "" } else { "\n " } ;
185+
186+ format ! (
187+ "{}{}\n {}{}" ,
188+ extends_line, extends_name, doc, blank_new_line
189+ )
190+ } ) ;
191+
174192 self
175193 }
176194
@@ -233,7 +251,7 @@ impl Formatter {
233251 /// outside of strings (simple or multiline).
234252 /// Use this to make post-processing changes needed for formatting but that
235253 /// shouldn't affect strings in the source code.
236- fn regex_replace_all_outside_strings ( & mut self , re : Regex , rep : & str ) {
254+ fn regex_replace_all_outside_strings < R : Replacer > ( & mut self , re : Regex , mut rep : R ) {
237255 let mut iter = re. captures_iter ( & self . content ) . peekable ( ) ;
238256 if iter. peek ( ) . is_none ( ) {
239257 return ;
@@ -260,7 +278,7 @@ impl Formatter {
260278 }
261279
262280 let mut replacement = String :: new ( ) ;
263- capture . expand ( rep , & mut replacement) ;
281+ rep . replace_append ( & capture , & mut replacement) ;
264282
265283 let new_end_byte = start_byte + replacement. len ( ) ;
266284
0 commit comments