Skip to content

Commit 16beba3

Browse files
authored
Merge pull request #109 from shadr/remove-blank-line-between-extends-and-doc-comment
Refactor how new lines after extends are inserted
2 parents 6e530a8 + 8b6917c commit 16beba3

4 files changed

Lines changed: 78 additions & 16 deletions

File tree

queries/gdscript.scm

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,8 @@
8585
(class_definition) @prepend_hardline
8686
(class_definition extends: (extends_statement "extends" @prepend_space))
8787

88-
(source
89-
(class_name_statement
90-
extends: (extends_statement) @append_delimiter @append_hardline (#delimiter! "\n")))
91-
(source
92-
(extends_statement) @append_delimiter @append_hardline (#delimiter! "\n"))
88+
(source (class_name_statement extends: (extends_statement) @append_hardline))
89+
(source (extends_statement) @append_hardline)
9390
(source (class_name_statement (name) @append_hardline))
9491

9592
; CONST DEFINITIONS

src/formatter.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
//! rules have too much performance overhead when applied through Topiary.
1515
use std::{collections::VecDeque, io::BufWriter};
1616

17-
use regex::{Regex, RegexBuilder};
17+
use regex::{Regex, RegexBuilder, Replacer};
1818
use topiary_core::{Language, Operation, TopiaryQuery, formatter_tree};
1919
use 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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
class_name Player
2+
extends CharacterBody2D
3+
## A brief description of the class's role and functionality.
4+
##
5+
## The description of script
6+
class_name Player
7+
extends CharacterBody2D
8+
## A brief description of the class's role and functionality.
9+
##
10+
## The description of script
11+
class_name Player
12+
extends CharacterBody2D
13+
## A brief description of the class's role and functionality.
14+
##
15+
## The description of script
16+
17+
var a = 10
18+
class_name Player
19+
extends CharacterBody2D
20+
## A brief description of the class's role and functionality.
21+
##
22+
## The description of script
23+
24+
var a = 10

tests/input/class_doc_comment.gd

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class_name Player extends CharacterBody2D
2+
3+
## A brief description of the class's role and functionality.
4+
##
5+
## The description of script
6+
class_name Player extends CharacterBody2D
7+
## A brief description of the class's role and functionality.
8+
##
9+
## The description of script
10+
class_name Player extends CharacterBody2D
11+
## A brief description of the class's role and functionality.
12+
##
13+
## The description of script
14+
15+
var a = 10
16+
class_name Player
17+
extends CharacterBody2D
18+
19+
## A brief description of the class's role and functionality.
20+
##
21+
## The description of script
22+
23+
var a = 10

0 commit comments

Comments
 (0)