Skip to content

Commit

Permalink
Allow configuring the script filename casing rule
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
RedMser authored and akien-mga committed Mar 5, 2024
1 parent a07dd0d commit 2bd714e
Show file tree
Hide file tree
Showing 15 changed files with 103 additions and 57 deletions.
7 changes: 7 additions & 0 deletions core/object/script_language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions core/object/script_language.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ class ScriptCodeCompletionCache {

class ScriptLanguage : public Object {
GDCLASS(ScriptLanguage, Object)

protected:
static void _bind_methods();

public:
virtual String get_name() const = 0;

Expand Down Expand Up @@ -224,6 +228,13 @@ class ScriptLanguage : public Object {
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;
Expand Down Expand Up @@ -260,6 +271,7 @@ class ScriptLanguage : public Object {
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
Expand Down Expand Up @@ -405,6 +417,8 @@ class ScriptLanguage : public Object {
virtual ~ScriptLanguage() {}
};

VARIANT_ENUM_CAST(ScriptLanguage::ScriptNameCasing);

extern uint8_t script_encryption_key[32];

class PlaceHolderScriptInstance : public ScriptInstance {
Expand Down
1 change: 1 addition & 0 deletions core/object/script_language_extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
1 change: 1 addition & 0 deletions core/object/script_language_extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ class ScriptLanguageExtension : public ScriptLanguage {
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 *)

Expand Down
7 changes: 5 additions & 2 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions doc/classes/ScriptLanguage.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 5 additions & 0 deletions doc/classes/ScriptLanguageExtension.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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*" />
Expand Down
2 changes: 1 addition & 1 deletion editor/editor_autoload_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())) {
Expand Down
31 changes: 27 additions & 4 deletions editor/editor_node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
7 changes: 5 additions & 2 deletions editor/editor_node.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -135,7 +136,8 @@ class EditorNode : public Node {
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 {
Expand Down Expand Up @@ -689,7 +691,8 @@ class EditorNode : public Node {
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);
Expand Down
4 changes: 3 additions & 1 deletion editor/register_editor_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down
63 changes: 18 additions & 45 deletions editor/script_create_dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand All @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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()));
Expand Down Expand Up @@ -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));

Expand Down
3 changes: 1 addition & 2 deletions editor/script_create_dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions modules/mono/csharp_script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions modules/mono/csharp_script.h
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ class CSharpLanguage : public ScriptLanguage {
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;
Expand Down

0 comments on commit 2bd714e

Please sign in to comment.