Allow configuring the script filename casing rule

Defaults to "Auto", which detects the casing based on the
preference of the currently selected language (C# for example
prefers PascalCase whereas GDScript prefers snake_case).
This commit is contained in:
RedMser 2023-06-11 18:23:48 +02:00 committed by Rémi Verschelde
parent a07dd0d6a5
commit 2bd714e34e
No known key found for this signature in database
GPG Key ID: C3336907360768E1
15 changed files with 103 additions and 57 deletions

View File

@ -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;

View File

@ -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 {

View File

@ -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");

View File

@ -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 *)

View File

@ -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.

View File

@ -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>

View File

@ -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*" />

View File

@ -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())) {

View File

@ -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() {

View File

@ -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);

View File

@ -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);

View File

@ -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 = _adjust_file_path(path);
_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);
}
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));

View File

@ -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);

View File

@ -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;

View File

@ -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;