Skip to content

Commit 9b1fff0

Browse files
committed
Add support for excluding files and folders
Close #299
1 parent ba01944 commit 9b1fff0

5 files changed

Lines changed: 92 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
This file documents the changes made to the formatter with each release.
44

5+
## Unreleased 0.24.0
6+
7+
### Added
8+
9+
- You can not exclude files and folders using the `--exclude/-x` flag or `gdscript_formatter_exclude` in your editorconfig files (#299)
10+
511
## Release 0.23.0 (2026-07-24)
612

713
### Added

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ To see other possible options, run `gdscript-formatter --help`.
8585

8686
You can also configure the formatter with an [EditorConfig](https://editorconfig.org/) file at the root of your project. This is a good way to share the same formatting settings with your whole team. The formatter supports the standard keys `indent_style`, `indent_size`, `max_line_length`, `insert_final_newline`, and `trim_trailing_whitespace`, plus custom keys prefixed with `gdscript_formatter_`. See the [GDScript Formatter docs](https://www.gdquest.com/library/gdscript_formatter/) for the complete list. Note that command line flags override `.editorconfig` values.
8787

88+
To exclude files or directories, pass `--exclude` (or `-x`) one or more times, for example `gdscript-formatter . -x addons`. You can also exclude files matched by an EditorConfig section with `gdscript_formatter_exclude = true`.
89+
8890
Use `--quote-style preserve/single/double` to automatically normalize the string quote style. You can also set the style in your `.editorconfig` file using the key `gdscript_formatter_quote_style`. The default value, `preserve`, leaves existing quotes unchanged.
8991

9092
The Godot add-on also reads `gdscript_formatter_format_on_save` from the `.editorconfig` file. This key only affects the add-on and enables or disables format on save for the whole project and overrides each user's add-on setting. For example:

src/cli.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const HELP_FORMATTER: &str = "\
2020
2121
Options:
2222
-c, --check Check if files are formatted, exit 1 if not
23+
-x, --exclude <PATH> Exclude one file or directory (you can repeat this option multiple times)
2324
--verify-structure Verify formatted output has the same structure as the input
2425
--stdout Write to stdout instead of overwriting files
2526
--use-spaces Use spaces instead of tabs for indentation
@@ -47,6 +48,7 @@ Arguments:
4748
<FILES>... GDScript files or directories to lint
4849
4950
Options:
51+
-x, --exclude <PATH> Exclude a file or directory (may be repeated)
5052
--disable <RULES> Disable specific rules (comma-separated)
5153
--max-line-length <NUM> Maximum line length allowed (default: 100)
5254
--list-rules List all available linting rules
@@ -59,6 +61,8 @@ Options:
5961
pub struct CliArguments {
6062
/// List of input file paths or directories to process.
6163
pub input_file_paths: Vec<PathBuf>,
64+
/// Files or directories to skip during discovery.
65+
pub excluded_paths: Vec<PathBuf>,
6266
/// Which command to run.
6367
pub command: Command,
6468
}
@@ -124,6 +128,7 @@ pub fn parse_args() -> CliArguments {
124128
let mut active_command = ActiveCommand::Format;
125129

126130
let mut input_file_paths: Vec<PathBuf> = Vec::new();
131+
let mut excluded_paths: Vec<PathBuf> = Vec::new();
127132
let mut format_do_print_to_stdout = false;
128133
let mut format_do_check_formatted_only = false;
129134
let mut format_use_spaces: Option<bool> = None;
@@ -179,6 +184,15 @@ pub fn parse_args() -> CliArguments {
179184
let (flag_name, assigned_value) = split_flag_and_value(flag_without_prefix);
180185
match active_command {
181186
ActiveCommand::Format => match flag_name {
187+
"exclude" => {
188+
let value = consume_flag_value(
189+
assigned_value,
190+
&argument_list,
191+
&mut current_argument_index,
192+
"--exclude",
193+
);
194+
excluded_paths.push(PathBuf::from(value));
195+
}
182196
"stdout" => {
183197
require_no_value(assigned_value, "--stdout");
184198
format_do_print_to_stdout = true;
@@ -288,6 +302,15 @@ pub fn parse_args() -> CliArguments {
288302
)),
289303
},
290304
ActiveCommand::Lint => match flag_name {
305+
"exclude" => {
306+
let value = consume_flag_value(
307+
assigned_value,
308+
&argument_list,
309+
&mut current_argument_index,
310+
"--exclude",
311+
);
312+
excluded_paths.push(PathBuf::from(value));
313+
}
291314
"disable" => {
292315
let value = consume_flag_value(
293316
assigned_value,
@@ -328,6 +351,17 @@ pub fn parse_args() -> CliArguments {
328351
}
329352
} else if current_argument.starts_with('-') && current_argument.len() > 1 {
330353
let short_flags = &current_argument[1..];
354+
if short_flags == "x" {
355+
let value = consume_flag_value(
356+
None,
357+
&argument_list,
358+
&mut current_argument_index,
359+
"-x/--exclude",
360+
);
361+
excluded_paths.push(PathBuf::from(value));
362+
current_argument_index += 1;
363+
continue;
364+
}
331365
for flag_char in short_flags.chars() {
332366
match flag_char {
333367
'c' => {
@@ -357,6 +391,7 @@ pub fn parse_args() -> CliArguments {
357391
match active_command {
358392
ActiveCommand::Format => CliArguments {
359393
input_file_paths,
394+
excluded_paths,
360395
command: Command::Format {
361396
do_print_to_stdout: format_do_print_to_stdout,
362397
do_check_formatted_only: format_do_check_formatted_only,
@@ -372,6 +407,7 @@ pub fn parse_args() -> CliArguments {
372407
},
373408
ActiveCommand::Lint => CliArguments {
374409
input_file_paths,
410+
excluded_paths,
375411
command: Command::Lint {
376412
disabled_linter_rules: lint_disabled_rules,
377413
max_line_length: lint_max_line_length,

src/editorconfig.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ fn load_editorconfig_properties(editorconfig_file_path: &Path) -> Option<Propert
1616
Some(properties)
1717
}
1818

19+
/// Returns whether the matching EditorConfig settings exclude this file.
20+
pub fn is_excluded_by_editorconfig(editorconfig_file_path: &Path) -> bool {
21+
let Some(properties) = load_editorconfig_properties(editorconfig_file_path) else {
22+
return false;
23+
};
24+
properties
25+
.get_raw_for_key("gdscript_formatter_exclude")
26+
.into_option()
27+
.is_some_and(|value| value == "true")
28+
}
29+
1930
fn get_max_line_length_from_properties(properties: &Properties) -> Option<usize> {
2031
match properties.get::<MaxLineLen>() {
2132
Ok(MaxLineLen::Value(max_line_length)) if max_line_length > 0 => Some(max_line_length),

src/main.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
9595
max_line_length: max_line_length.unwrap_or(100),
9696
};
9797

98-
let input_gdscript_files = find_gdscript_files(&parsed_cli_args.input_file_paths)?;
98+
let input_gdscript_files = find_gdscript_files(
99+
&parsed_cli_args.input_file_paths,
100+
&parsed_cli_args.excluded_paths,
101+
)?;
99102
return run_linter(
100103
&input_gdscript_files,
101104
linter_config,
@@ -180,7 +183,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
180183
} else {
181184
parsed_cli_args.input_file_paths
182185
};
183-
let input_gdscript_files = find_gdscript_files(&input_paths)?;
186+
let input_gdscript_files = find_gdscript_files(&input_paths, &parsed_cli_args.excluded_paths)?;
184187

185188
let total_files = input_gdscript_files.len();
186189

@@ -414,6 +417,7 @@ fn format_chunk(
414417

415418
fn find_gdscript_files(
416419
input_paths: &[PathBuf],
420+
excluded_paths: &[PathBuf],
417421
) -> Result<Vec<PathBuf>, Box<dyn std::error::Error>> {
418422
let mut gdscript_file_paths = Vec::new();
419423
let mut paths_to_check: Vec<PathBuf> = Vec::with_capacity(input_paths.len());
@@ -422,6 +426,9 @@ fn find_gdscript_files(
422426
}
423427

424428
while let Some(current_path) = paths_to_check.pop() {
429+
if is_path_excluded(&current_path, excluded_paths) {
430+
continue;
431+
}
425432
if current_path.is_dir() {
426433
let entries = fs::read_dir(&current_path).map_err(|error| {
427434
format!(
@@ -442,13 +449,22 @@ fn find_gdscript_files(
442449
paths_to_check.push(entry.path());
443450
} else if let Some(extension) = entry.path().extension() {
444451
if extension == "gd" {
445-
gdscript_file_paths.push(entry.path());
452+
let file_path = entry.path();
453+
if !is_path_excluded(&file_path, excluded_paths)
454+
&& !gdscript_formatter::editorconfig::is_excluded_by_editorconfig(
455+
&file_path,
456+
)
457+
{
458+
gdscript_file_paths.push(file_path);
459+
}
446460
}
447461
}
448462
}
449463
} else if let Some(extension) = current_path.extension() {
450464
if extension == "gd" {
451-
gdscript_file_paths.push(current_path);
465+
if !gdscript_formatter::editorconfig::is_excluded_by_editorconfig(&current_path) {
466+
gdscript_file_paths.push(current_path);
467+
}
452468
}
453469
}
454470
}
@@ -466,6 +482,23 @@ fn find_gdscript_files(
466482
Ok(gdscript_file_paths)
467483
}
468484

485+
fn is_path_excluded(path: &Path, excluded_paths: &[PathBuf]) -> bool {
486+
let current_directory = env::current_dir().expect("Failed to get current directory");
487+
let absolute_path = if path.is_absolute() {
488+
path.to_path_buf()
489+
} else {
490+
current_directory.join(path)
491+
};
492+
excluded_paths.iter().any(|exclude_path| {
493+
let absolute_exclude_path = if exclude_path.is_absolute() {
494+
exclude_path.clone()
495+
} else {
496+
current_directory.join(exclude_path)
497+
};
498+
absolute_path.starts_with(absolute_exclude_path)
499+
})
500+
}
501+
469502
fn compare_output_index(
470503
left: &Result<FormatterOutput, String>,
471504
right: &Result<FormatterOutput, String>,

0 commit comments

Comments
 (0)