浏览代码

Merge pull request #78119 from RedMser/script-filename-casing

Allow configuring the script filename casing rule
Rémi Verschelde 1 年之前
父节点
当前提交
897e2d9a40

+ 7 - 0
core/object/script_language.cpp

@@ -535,6 +535,13 @@ TypedArray<int> ScriptLanguage::CodeCompletionOption::get_option_cached_characte
 	return charac;
 }
 
+void ScriptLanguage::_bind_methods() {
+	BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_AUTO);
+	BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_PASCAL_CASE);
+	BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_SNAKE_CASE);
+	BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_KEBAB_CASE);
+}
+
 bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) {
 	if (script->is_placeholder_fallback_enabled()) {
 		return false;

+ 14 - 0
core/object/script_language.h

@@ -193,6 +193,10 @@ public:
 
 class ScriptLanguage : public Object {
 	GDCLASS(ScriptLanguage, Object)
+
+protected:
+	static void _bind_methods();
+
 public:
 	virtual String get_name() const = 0;
 
@@ -224,6 +228,13 @@ public:
 		TEMPLATE_PROJECT
 	};
 
+	enum ScriptNameCasing {
+		SCRIPT_NAME_CASING_AUTO,
+		SCRIPT_NAME_CASING_PASCAL_CASE,
+		SCRIPT_NAME_CASING_SNAKE_CASE,
+		SCRIPT_NAME_CASING_KEBAB_CASE,
+	};
+
 	struct ScriptTemplate {
 		String inherit = "Object";
 		String name;
@@ -260,6 +271,7 @@ public:
 	virtual bool can_make_function() const { return true; }
 	virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; }
 	virtual bool overrides_external_editor() { return false; }
+	virtual ScriptNameCasing preferred_file_name_casing() const { return SCRIPT_NAME_CASING_SNAKE_CASE; }
 
 	// Keep enums in sync with:
 	// scene/gui/code_edit.h - CodeEdit::CodeCompletionKind
@@ -405,6 +417,8 @@ public:
 	virtual ~ScriptLanguage() {}
 };
 
+VARIANT_ENUM_CAST(ScriptLanguage::ScriptNameCasing);
+
 extern uint8_t script_encryption_key[32];
 
 class PlaceHolderScriptInstance : public ScriptInstance {

+ 1 - 0
core/object/script_language_extension.cpp

@@ -112,6 +112,7 @@ void ScriptLanguageExtension::_bind_methods() {
 	GDVIRTUAL_BIND(_can_make_function);
 	GDVIRTUAL_BIND(_open_in_external_editor, "script", "line", "column");
 	GDVIRTUAL_BIND(_overrides_external_editor);
+	GDVIRTUAL_BIND(_preferred_file_name_casing);
 
 	GDVIRTUAL_BIND(_complete_code, "code", "path", "owner");
 	GDVIRTUAL_BIND(_lookup_code, "code", "symbol", "path", "owner");

+ 1 - 0
core/object/script_language_extension.h

@@ -376,6 +376,7 @@ public:
 	EXBIND0RC(bool, can_make_function)
 	EXBIND3R(Error, open_in_external_editor, const Ref<Script> &, int, int)
 	EXBIND0R(bool, overrides_external_editor)
+	EXBIND0RC(ScriptNameCasing, preferred_file_name_casing)
 
 	GDVIRTUAL3RC(Dictionary, _complete_code, const String &, const String &, Object *)
 

+ 5 - 2
doc/classes/ProjectSettings.xml

@@ -948,13 +948,16 @@
 			The format of the default signal callback name when a signal connects to the same node that emits it (in the Signal Connection Dialog). The following substitutions are available: [code]{NodeName}[/code], [code]{nodeName}[/code], [code]{node_name}[/code], [code]{SignalName}[/code], [code]{signalName}[/code], and [code]{signal_name}[/code].
 		</member>
 		<member name="editor/naming/node_name_casing" type="int" setter="" getter="" default="0">
-			When creating node names automatically, set the type of casing in this project. This is mostly an editor setting.
+			When creating node names automatically, set the type of casing to use in this project. This is mostly an editor setting.
 		</member>
 		<member name="editor/naming/node_name_num_separator" type="int" setter="" getter="" default="0">
 			What to use to separate node name from number. This is mostly an editor setting.
 		</member>
 		<member name="editor/naming/scene_name_casing" type="int" setter="" getter="" default="2">
-			When generating file names from scene root node, set the type of casing in this project. This is mostly an editor setting.
+			When generating scene file names from scene root node, set the type of casing to use in this project. This is mostly an editor setting.
+		</member>
+		<member name="editor/naming/script_name_casing" type="int" setter="" getter="" default="0">
+			When generating script file names from the selected node, set the type of casing to use in this project. This is mostly an editor setting.
 		</member>
 		<member name="editor/run/main_run_args" type="String" setter="" getter="" default="&quot;&quot;">
 			The command-line arguments to append to Godot's own command line when running the project. This doesn't affect the editor itself.

+ 10 - 0
doc/classes/ScriptLanguage.xml

@@ -6,4 +6,14 @@
 	</description>
 	<tutorials>
 	</tutorials>
+	<constants>
+		<constant name="SCRIPT_NAME_CASING_AUTO" value="0" enum="ScriptNameCasing">
+		</constant>
+		<constant name="SCRIPT_NAME_CASING_PASCAL_CASE" value="1" enum="ScriptNameCasing">
+		</constant>
+		<constant name="SCRIPT_NAME_CASING_SNAKE_CASE" value="2" enum="ScriptNameCasing">
+		</constant>
+		<constant name="SCRIPT_NAME_CASING_KEBAB_CASE" value="3" enum="ScriptNameCasing">
+		</constant>
+	</constants>
 </class>

+ 5 - 0
doc/classes/ScriptLanguageExtension.xml

@@ -267,6 +267,11 @@
 			<description>
 			</description>
 		</method>
+		<method name="_preferred_file_name_casing" qualifiers="virtual const">
+			<return type="int" enum="ScriptLanguage.ScriptNameCasing" />
+			<description>
+			</description>
+		</method>
 		<method name="_profiling_get_accumulated_data" qualifiers="virtual">
 			<return type="int" />
 			<param index="0" name="info_array" type="ScriptLanguageExtensionProfilingInfo*" />

+ 1 - 1
editor/editor_autoload_settings.cpp

@@ -166,7 +166,7 @@ void EditorAutoloadSettings::_autoload_add() {
 		if (!fpath.ends_with("/")) {
 			fpath = fpath.get_base_dir();
 		}
-		dialog->config("Node", fpath.path_join(vformat("%s.gd", autoload_add_name->get_text().to_snake_case())), false, false);
+		dialog->config("Node", fpath.path_join(vformat("%s.gd", autoload_add_name->get_text())), false, false);
 		dialog->popup_centered();
 	} else {
 		if (autoload_add(autoload_add_name->get_text(), autoload_add_path->get_text())) {

+ 27 - 4
editor/editor_node.cpp

@@ -3089,17 +3089,40 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 	}
 }
 
-String EditorNode::adjust_scene_name_casing(const String &root_name) {
+String EditorNode::adjust_scene_name_casing(const String &p_root_name) {
 	switch (GLOBAL_GET("editor/naming/scene_name_casing").operator int()) {
 		case SCENE_NAME_CASING_AUTO:
 			// Use casing of the root node.
 			break;
 		case SCENE_NAME_CASING_PASCAL_CASE:
-			return root_name.to_pascal_case();
+			return p_root_name.replace("-", "_").to_pascal_case();
 		case SCENE_NAME_CASING_SNAKE_CASE:
-			return root_name.replace("-", "_").to_snake_case();
+			return p_root_name.replace("-", "_").to_snake_case();
+		case SCENE_NAME_CASING_KEBAB_CASE:
+			return p_root_name.to_snake_case().replace("_", "-");
 	}
-	return root_name;
+	return p_root_name;
+}
+
+String EditorNode::adjust_script_name_casing(const String &p_file_name, ScriptLanguage::ScriptNameCasing p_auto_casing) {
+	int editor_casing = GLOBAL_GET("editor/naming/script_name_casing");
+	if (editor_casing == ScriptLanguage::SCRIPT_NAME_CASING_AUTO) {
+		// Use the script language's preferred casing.
+		editor_casing = p_auto_casing;
+	}
+
+	switch (editor_casing) {
+		case ScriptLanguage::SCRIPT_NAME_CASING_AUTO:
+			// Script language has no preference, so do not adjust.
+			break;
+		case ScriptLanguage::SCRIPT_NAME_CASING_PASCAL_CASE:
+			return p_file_name.replace("-", "_").to_pascal_case();
+		case ScriptLanguage::SCRIPT_NAME_CASING_SNAKE_CASE:
+			return p_file_name.replace("-", "_").to_snake_case();
+		case ScriptLanguage::SCRIPT_NAME_CASING_KEBAB_CASE:
+			return p_file_name.to_snake_case().replace("_", "-");
+	}
+	return p_file_name;
 }
 
 void EditorNode::_request_screenshot() {

+ 5 - 2
editor/editor_node.h

@@ -31,6 +31,7 @@
 #ifndef EDITOR_NODE_H
 #define EDITOR_NODE_H
 
+#include "core/object/script_language.h"
 #include "core/templates/safe_refcount.h"
 #include "editor/editor_data.h"
 #include "editor/editor_folding.h"
@@ -135,7 +136,8 @@ public:
 	enum SceneNameCasing {
 		SCENE_NAME_CASING_AUTO,
 		SCENE_NAME_CASING_PASCAL_CASE,
-		SCENE_NAME_CASING_SNAKE_CASE
+		SCENE_NAME_CASING_SNAKE_CASE,
+		SCENE_NAME_CASING_KEBAB_CASE,
 	};
 
 	struct ExecuteThreadArgs {
@@ -689,7 +691,8 @@ public:
 	static VSplitContainer *get_top_split() { return singleton->top_split; }
 	static EditorBottomPanel *get_bottom_panel() { return singleton->bottom_panel; }
 
-	static String adjust_scene_name_casing(const String &root_name);
+	static String adjust_scene_name_casing(const String &p_root_name);
+	static String adjust_script_name_casing(const String &p_file_name, ScriptLanguage::ScriptNameCasing p_auto_casing);
 
 	static bool has_unsaved_changes() { return singleton->unsaved_cache; }
 	static void disambiguate_filenames(const Vector<String> p_full_paths, Vector<String> &r_filenames);

+ 3 - 1
editor/register_editor_types.cpp

@@ -30,6 +30,7 @@
 
 #include "register_editor_types.h"
 
+#include "core/object/script_language.h"
 #include "editor/debugger/debug_adapter/debug_adapter_server.h"
 #include "editor/editor_command_palette.h"
 #include "editor/editor_feature_profile.h"
@@ -269,7 +270,8 @@ void register_editor_types() {
 
 	GLOBAL_DEF("editor/naming/default_signal_callback_name", "_on_{node_name}_{signal_name}");
 	GLOBAL_DEF("editor/naming/default_signal_callback_to_self_name", "_on_{signal_name}");
-	GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), EditorNode::SCENE_NAME_CASING_SNAKE_CASE);
+	GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case,kebab-case"), EditorNode::SCENE_NAME_CASING_SNAKE_CASE);
+	GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/script_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case,kebab-case"), ScriptLanguage::SCRIPT_NAME_CASING_AUTO);
 
 	GLOBAL_DEF("editor/import/reimport_missing_imported_files", true);
 	GLOBAL_DEF("editor/import/use_multiple_threads", true);

+ 18 - 45
editor/script_create_dialog.cpp

@@ -125,7 +125,6 @@ void ScriptCreateDialog::_notification(int p_what) {
 				for (int i = 0; i < language_menu->get_item_count(); i++) {
 					if (language_menu->get_item_text(i) == last_language) {
 						language_menu->select(i);
-						current_language = i;
 						break;
 					}
 				}
@@ -146,8 +145,8 @@ void ScriptCreateDialog::_notification(int p_what) {
 
 void ScriptCreateDialog::_path_hbox_sorted() {
 	if (is_visible()) {
-		int filename_start_pos = initial_bp.rfind("/") + 1;
-		int filename_end_pos = initial_bp.length();
+		int filename_start_pos = file_path->get_text().rfind("/") + 1;
+		int filename_end_pos = file_path->get_text().length();
 
 		if (!is_built_in) {
 			file_path->select(filename_start_pos, filename_end_pos);
@@ -166,26 +165,30 @@ bool ScriptCreateDialog::_can_be_built_in() {
 	return (supports_built_in && built_in_enabled);
 }
 
+String ScriptCreateDialog::_adjust_file_path(const String &p_base_path) const {
+	if (p_base_path.is_empty()) {
+		return p_base_path;
+	}
+
+	String base_dir = p_base_path.get_base_dir();
+	String file_name = p_base_path.get_file().get_basename();
+	file_name = EditorNode::adjust_script_name_casing(file_name, language->preferred_file_name_casing());
+	String extension = language->get_extension();
+	return base_dir.path_join(file_name + "." + extension);
+}
+
 void ScriptCreateDialog::config(const String &p_base_name, const String &p_base_path, bool p_built_in_enabled, bool p_load_enabled) {
 	parent_name->set_text(p_base_name);
 	parent_name->deselect();
 	built_in_name->set_text("");
 
-	if (!p_base_path.is_empty()) {
-		initial_bp = p_base_path.get_basename();
-		file_path->set_text(initial_bp + "." + ScriptServer::get_language(language_menu->get_selected())->get_extension());
-		current_language = language_menu->get_selected();
-	} else {
-		initial_bp = "";
-		file_path->set_text("");
-	}
+	file_path->set_text(p_base_path);
 	file_path->deselect();
 
 	built_in_enabled = p_built_in_enabled;
 	load_enabled = p_load_enabled;
 
-	_language_changed(current_language);
-	_path_changed(file_path->get_text());
+	_language_changed(language_menu->get_selected());
 }
 
 void ScriptCreateDialog::set_inheritance_base_type(const String &p_base) {
@@ -388,38 +391,9 @@ void ScriptCreateDialog::_language_changed(int l) {
 		is_built_in = false;
 	}
 
-	String selected_ext = "." + language->get_extension();
 	String path = file_path->get_text();
-	String extension = "";
-	if (!path.is_empty()) {
-		if (path.contains(".")) {
-			extension = path.get_extension();
-		}
-
-		if (extension.length() == 0) {
-			// Add extension if none.
-			path += selected_ext;
-			_path_changed(path);
-		} else {
-			// Change extension by selected language.
-			List<String> extensions;
-			// Get all possible extensions for script.
-			for (int m = 0; m < language_menu->get_item_count(); m++) {
-				ScriptServer::get_language(m)->get_recognized_extensions(&extensions);
-			}
-
-			for (const String &E : extensions) {
-				if (E.nocasecmp_to(extension) == 0) {
-					path = path.get_basename() + selected_ext;
-					_path_changed(path);
-					break;
-				}
-			}
-		}
-	} else {
-		path = "class" + selected_ext;
-		_path_changed(path);
-	}
+	path = _adjust_file_path(path);
+	_path_changed(path);
 	file_path->set_text(path);
 
 	EditorSettings::get_singleton()->set_project_metadata("script_setup", "last_selected_language", language_menu->get_item_text(language_menu->get_selected()));
@@ -896,7 +870,6 @@ ScriptCreateDialog::ScriptCreateDialog() {
 	if (default_language >= 0) {
 		language_menu->select(default_language);
 	}
-	current_language = default_language;
 
 	language_menu->connect("item_selected", callable_mp(this, &ScriptCreateDialog::_language_changed));
 

+ 1 - 2
editor/script_create_dialog.h

@@ -71,7 +71,6 @@ class ScriptCreateDialog : public ConfirmationDialog {
 	bool is_browsing_parent = false;
 	String path_error;
 	String template_inactive_message;
-	String initial_bp;
 	bool is_new_script_created = true;
 	bool is_path_valid = false;
 	bool supports_built_in = false;
@@ -82,7 +81,6 @@ class ScriptCreateDialog : public ConfirmationDialog {
 	bool is_using_templates = true;
 	bool built_in_enabled = true;
 	bool load_enabled = true;
-	int current_language;
 	int default_language;
 	bool re_check_path = false;
 
@@ -117,6 +115,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
 	Vector<ScriptLanguage::ScriptTemplate> _get_user_templates(const ScriptLanguage *p_language, const StringName &p_object, const String &p_dir, const ScriptLanguage::TemplateLocation &p_origin) const;
 	ScriptLanguage::ScriptTemplate _parse_template(const ScriptLanguage *p_language, const String &p_path, const String &p_filename, const ScriptLanguage::TemplateLocation &p_origin, const String &p_inherits) const;
 	String _get_script_origin_label(const ScriptLanguage::TemplateLocation &p_origin) const;
+	String _adjust_file_path(const String &p_base_path) const;
 
 protected:
 	void _notification(int p_what);

+ 4 - 0
modules/mono/csharp_script.cpp

@@ -405,6 +405,10 @@ bool CSharpLanguage::supports_builtin_mode() const {
 	return false;
 }
 
+ScriptLanguage::ScriptNameCasing CSharpLanguage::preferred_file_name_casing() const {
+	return SCRIPT_NAME_CASING_PASCAL_CASE;
+}
+
 #ifdef TOOLS_ENABLED
 struct VariantCsName {
 	Variant::Type variant_type;

+ 1 - 0
modules/mono/csharp_script.h

@@ -518,6 +518,7 @@ public:
 	virtual String _get_indentation() const;
 	/* TODO? */ void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {}
 	/* TODO */ void add_global_constant(const StringName &p_variable, const Variant &p_value) override {}
+	virtual ScriptNameCasing preferred_file_name_casing() const override;
 
 	/* SCRIPT GLOBAL CLASS FUNCTIONS */
 	virtual bool handles_global_class_type(const String &p_type) const override;