From adeee5e5c77abcacb799ffb9ee94cf9388f4b820 Mon Sep 17 00:00:00 2001 From: Noah Greer Date: Mon, 10 Aug 2026 13:50:31 -0700 Subject: [PATCH 1/5] fix(plugin): resolve ignored directories never matching due to trailing slash Fixes a bug introduced in #191 Directory paths with a trailing "/" (e.g. "addons/") would produce a trailing empty string when split on "/", which could never match a real path segment. This silently broke the ignore rule for any entry with a trailing slash. This fix trims the trailing "/" before splitting. --- addons/GDQuest_GDScript_formatter/plugin.gd | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addons/GDQuest_GDScript_formatter/plugin.gd b/addons/GDQuest_GDScript_formatter/plugin.gd index c59d9c7..70200b5 100644 --- a/addons/GDQuest_GDScript_formatter/plugin.gd +++ b/addons/GDQuest_GDScript_formatter/plugin.gd @@ -353,7 +353,10 @@ func _on_resource_saved(saved_resource: Resource) -> void: var script_path_parts := path.split("/") for directory: String in ignored_directories: - var normalized_dir := directory.trim_prefix("res://") + # Remove any trailing "/" so splitting doesn't leave an empty + # string at the end (e.g. "addons/" -> ["addons", ""]), which + # would never match a real path segment. + var normalized_dir := directory.trim_prefix("res://").trim_suffix("/") var directory_parts := normalized_dir.split("/") var matches := true From a062374a69b454e6723cd401d5f51efa2b98294b Mon Sep 17 00:00:00 2001 From: Noah Greer Date: Tue, 11 Aug 2026 19:39:30 -0700 Subject: [PATCH 2/5] fix(plugin): resolve out-of-bounds access when ignored directory is deeper than script path Fixes a bug introduced in #191 The matching loop indexed script_path_parts[i] using the length of directory_parts, without checking that directory_parts wasn't longer. If an ignored directory's path has more segments than the script's path, and every one of those extra segments follows a full match of the script's path, the loop runs past the end of script_path_parts. Example: script at "res://addons/foo.gd" (script_path_parts = ["addons", "foo.gd"]) checked against ignored directory "addons/foo.gd/bar" (directory_parts = ["addons", "foo.gd", "bar"]). The first two segments match, then the loop tries to read script_path_parts[2], which doesn't exist. Skip directories that have more segments than the script path, since they can never match as a prefix. --- addons/GDQuest_GDScript_formatter/plugin.gd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addons/GDQuest_GDScript_formatter/plugin.gd b/addons/GDQuest_GDScript_formatter/plugin.gd index 70200b5..24766e1 100644 --- a/addons/GDQuest_GDScript_formatter/plugin.gd +++ b/addons/GDQuest_GDScript_formatter/plugin.gd @@ -359,6 +359,12 @@ func _on_resource_saved(saved_resource: Resource) -> void: var normalized_dir := directory.trim_prefix("res://").trim_suffix("/") var directory_parts := normalized_dir.split("/") + # Skip this directory if it has more segments than the script's path. + # A directory with more segments than the script's path cannot be a prefix match, + # and comparing would index past the end of script_path_parts below. + if directory_parts.size() > script_path_parts.size(): + continue + var matches := true for i in range(directory_parts.size()): if directory_parts[i] != script_path_parts[i]: From 01865f08e16b59429524e7ef3feb40a1122a2e64 Mon Sep 17 00:00:00 2001 From: Noah Greer Date: Tue, 11 Aug 2026 22:40:42 -0700 Subject: [PATCH 3/5] fix(plugin): skip and warn on blank format on save ignored directories entries Fixes a bug introduced in #191 An ignored directory entry that is blank, "res://", or just "/" would normalize to an empty string. Splitting that on "/" produces [""], which could be compared against script path segments in unintended ways instead of being treated as an invalid entry. Skip these entries and push a warning so users know the entry has no usable path and needs to be removed or corrected. Also warns the user that if the intention was to ignore the whole project that this is not supported here and they need to turn of format on save instead. --- addons/GDQuest_GDScript_formatter/plugin.gd | 28 ++++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/addons/GDQuest_GDScript_formatter/plugin.gd b/addons/GDQuest_GDScript_formatter/plugin.gd index 24766e1..785617b 100644 --- a/addons/GDQuest_GDScript_formatter/plugin.gd +++ b/addons/GDQuest_GDScript_formatter/plugin.gd @@ -347,16 +347,36 @@ func _on_resource_saved(saved_resource: Resource) -> void: if not do_format_on_save and not lint_on_save: return - var ignored_directories = get_editor_setting(SETTING_IGNORED_DIRECTORIES) - var path = script.resource_path.trim_prefix("res://") - - var script_path_parts := path.split("/") + var ignored_directories: PackedStringArray = get_editor_setting(SETTING_IGNORED_DIRECTORIES) + # Normalize and validate every ignored directory entry before any matching happens. + # This guarantees blank entries are always warned about, regardless of their position in the list. + var normalized_dirs: Array[String] = [] for directory: String in ignored_directories: # Remove any trailing "/" so splitting doesn't leave an empty # string at the end (e.g. "addons/" -> ["addons", ""]), which # would never match a real path segment. var normalized_dir := directory.trim_prefix("res://").trim_suffix("/") + + # Skip blank entries (e.g. "", "res://", or "/") so we don't + # split into [""] and compare against an empty path segment. + if normalized_dir.is_empty(): + push_warning( + "GDScript Formatter: Format on Save Ignored Directories entry \"%s\" " % directory + + "has no path after removing \"res://\" and trailing slashes, and will be skipped. " + + "This may mean you're trying to ignore the entire project, which isn't supported here. " + + "Please remove it from the list, enter a valid path, or turn off format on save instead." + ) + continue + + normalized_dirs.push_back(normalized_dir) + + + var path = script.resource_path.trim_prefix("res://") + + var script_path_parts := path.split("/") + + for normalized_dir: String in normalized_dirs: var directory_parts := normalized_dir.split("/") # Skip this directory if it has more segments than the script's path. From 16ca2777ec19e56abcf3b856fd833812cb420673 Mon Sep 17 00:00:00 2001 From: Noah Greer Date: Wed, 12 Aug 2026 16:46:10 -0700 Subject: [PATCH 4/5] fix(plugin): warn on format on save ignored directories entries that duplicate an earlier entry after normalization Fixes a bug introduced in #191 The Format on Save Ignored Directories list was not checked for entries that normalize to the same path despite being written differently (e.g. "res://addons" and "addons/"). This could let redundant entries sit in the list unnoticed. Track each normalized entry as it's validated, and if a later entry normalizes to the same as one already seen, skip it and push a warning naming both the duplicate and the original entry it conflicts with. --- addons/GDQuest_GDScript_formatter/plugin.gd | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/addons/GDQuest_GDScript_formatter/plugin.gd b/addons/GDQuest_GDScript_formatter/plugin.gd index 785617b..1f58d89 100644 --- a/addons/GDQuest_GDScript_formatter/plugin.gd +++ b/addons/GDQuest_GDScript_formatter/plugin.gd @@ -352,6 +352,12 @@ func _on_resource_saved(saved_resource: Resource) -> void: # Normalize and validate every ignored directory entry before any matching happens. # This guarantees blank entries are always warned about, regardless of their position in the list. var normalized_dirs: Array[String] = [] + + # Tracks normalized directories we've already seen, mapped to the original + # raw entry that produced them, so duplicate warnings can reference + # both the current and the earlier conflicting entry. + var seen_normalized_dirs: Dictionary[String, String] = {} + for directory: String in ignored_directories: # Remove any trailing "/" so splitting doesn't leave an empty # string at the end (e.g. "addons/" -> ["addons", ""]), which @@ -369,6 +375,18 @@ func _on_resource_saved(saved_resource: Resource) -> void: ) continue + # Skip entries that are effectively the same directory as one already + # seen, even if written differently (e.g. "res://addons" and "addons/"). + var seen_normalized_dir: Variant = seen_normalized_dirs.get(normalized_dir) + if seen_normalized_dir != null: + push_warning( + "GDScript Formatter: Format on Save Ignored Directories entry \"%s\" " % directory + + "refers to the same directory as entry \"%s\" " % seen_normalized_dir + + "and will be skipped. Please remove the duplicate entry from the list." + ) + continue + + seen_normalized_dirs[normalized_dir] = directory normalized_dirs.push_back(normalized_dir) From a3b506a946c1613ec381bb75c0e6fc40c72e5c66 Mon Sep 17 00:00:00 2001 From: Nathan Lovato <12694995+NathanLovato@users.noreply.github.com> Date: Fri, 14 Aug 2026 11:34:51 +0200 Subject: [PATCH 5/5] refactor: trim comments, rename variables --- addons/GDQuest_GDScript_formatter/plugin.gd | 63 +++++++-------------- 1 file changed, 20 insertions(+), 43 deletions(-) diff --git a/addons/GDQuest_GDScript_formatter/plugin.gd b/addons/GDQuest_GDScript_formatter/plugin.gd index 1f58d89..b143eb3 100644 --- a/addons/GDQuest_GDScript_formatter/plugin.gd +++ b/addons/GDQuest_GDScript_formatter/plugin.gd @@ -173,7 +173,7 @@ func _enter_tree() -> void: installer = FormatterInstaller.new(formatter_cache_dir) add_child(installer) installer.installation_completed.connect( - func _on_installation_completed (binary_path: String) -> void: + func _on_installation_completed(binary_path: String) -> void: set_editor_setting(SETTING_FORMATTER_PATH, binary_path) _has_formatter_command = has_command(binary_path) if not _has_formatter_command: @@ -188,7 +188,7 @@ func _enter_tree() -> void: menu.update_menu(true), ) installer.installation_failed.connect( - func _on_installation_failed (error_message: String) -> void: + func _on_installation_failed(error_message: String) -> void: push_error("Formatter installation failed: ", error_message), ) @@ -347,26 +347,13 @@ func _on_resource_saved(saved_resource: Resource) -> void: if not do_format_on_save and not lint_on_save: return - var ignored_directories: PackedStringArray = get_editor_setting(SETTING_IGNORED_DIRECTORIES) - - # Normalize and validate every ignored directory entry before any matching happens. - # This guarantees blank entries are always warned about, regardless of their position in the list. - var normalized_dirs: Array[String] = [] - - # Tracks normalized directories we've already seen, mapped to the original - # raw entry that produced them, so duplicate warnings can reference - # both the current and the earlier conflicting entry. - var seen_normalized_dirs: Dictionary[String, String] = {} - - for directory: String in ignored_directories: - # Remove any trailing "/" so splitting doesn't leave an empty - # string at the end (e.g. "addons/" -> ["addons", ""]), which - # would never match a real path segment. - var normalized_dir := directory.trim_prefix("res://").trim_suffix("/") - - # Skip blank entries (e.g. "", "res://", or "/") so we don't - # split into [""] and compare against an empty path segment. - if normalized_dir.is_empty(): + var ignored_directories_normalized: Array[String] = [] + var ignored_directories_seen: Dictionary[String, String] = { } + for directory: String in get_editor_setting(SETTING_IGNORED_DIRECTORIES): + # We split paths on "/", we trim trailing slashes to avoid empty path + # segments that would never match when checking ignored directories. + var directory_normalized := directory.trim_prefix("res://").trim_suffix("/") + if directory_normalized.is_empty(): push_warning( "GDScript Formatter: Format on Save Ignored Directories entry \"%s\" " % directory + "has no path after removing \"res://\" and trailing slashes, and will be skipped. " @@ -375,37 +362,27 @@ func _on_resource_saved(saved_resource: Resource) -> void: ) continue - # Skip entries that are effectively the same directory as one already - # seen, even if written differently (e.g. "res://addons" and "addons/"). - var seen_normalized_dir: Variant = seen_normalized_dirs.get(normalized_dir) - if seen_normalized_dir != null: + if ignored_directories_seen.has(directory_normalized): push_warning( "GDScript Formatter: Format on Save Ignored Directories entry \"%s\" " % directory - + "refers to the same directory as entry \"%s\" " % seen_normalized_dir + + "refers to the same directory as entry \"%s\" " + % ignored_directories_seen[directory_normalized] + "and will be skipped. Please remove the duplicate entry from the list." ) continue - seen_normalized_dirs[normalized_dir] = directory - normalized_dirs.push_back(normalized_dir) - - - var path = script.resource_path.trim_prefix("res://") - - var script_path_parts := path.split("/") - - for normalized_dir: String in normalized_dirs: - var directory_parts := normalized_dir.split("/") + ignored_directories_seen[directory_normalized] = directory + ignored_directories_normalized.push_back(directory_normalized) - # Skip this directory if it has more segments than the script's path. - # A directory with more segments than the script's path cannot be a prefix match, - # and comparing would index past the end of script_path_parts below. - if directory_parts.size() > script_path_parts.size(): + var saved_script_path_segments := script.resource_path.trim_prefix("res://").split("/") + for ignored_directory_path: String in ignored_directories_normalized: + var ignored_directory_segments := ignored_directory_path.split("/") + if ignored_directory_segments.size() > saved_script_path_segments.size(): continue var matches := true - for i in range(directory_parts.size()): - if directory_parts[i] != script_path_parts[i]: + for i in range(ignored_directory_segments.size()): + if ignored_directory_segments[i] != saved_script_path_segments[i]: matches = false break