feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -31,15 +31,19 @@
#include "script_text_editor.h"
#include "core/config/project_settings.h"
#include "core/io/json.h"
#include "core/math/expression.h"
#include "core/os/keyboard.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_toaster.h"
#include "editor/plugins/editor_context_menu_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/rich_text_label.h"
#include "scene/gui/split_container.h"
@ -207,7 +211,7 @@ void ScriptTextEditor::_load_theme_settings() {
}
theme_loaded = true;
if (!script.is_null()) {
if (script.is_valid()) {
_set_theme_for_script();
}
}
@ -282,7 +286,7 @@ void ScriptTextEditor::_warning_clicked(const Variant &p_line) {
CodeEdit *text_editor = code_editor->get_text_editor();
String prev_line = line > 0 ? text_editor->get_line(line - 1) : "";
if (prev_line.contains("@warning_ignore")) {
const int closing_bracket_idx = prev_line.find(")");
const int closing_bracket_idx = prev_line.find_char(')');
const String text_to_insert = ", " + code.quote(quote_style);
text_editor->insert_text(text_to_insert, line - 1, closing_bracket_idx);
} else {
@ -302,19 +306,17 @@ void ScriptTextEditor::_warning_clicked(const Variant &p_line) {
void ScriptTextEditor::_error_clicked(const Variant &p_line) {
if (p_line.get_type() == Variant::INT) {
code_editor->get_text_editor()->remove_secondary_carets();
code_editor->get_text_editor()->set_caret_line(p_line.operator int64_t());
goto_line_centered(p_line.operator int64_t());
} else if (p_line.get_type() == Variant::DICTIONARY) {
Dictionary meta = p_line.operator Dictionary();
const String path = meta["path"].operator String();
const int line = meta["line"].operator int64_t();
const int column = meta["column"].operator int64_t();
if (path.is_empty()) {
code_editor->get_text_editor()->remove_secondary_carets();
code_editor->get_text_editor()->set_caret_line(line);
goto_line_centered(line, column);
} else {
Ref<Resource> scr = ResourceLoader::load(path);
if (!scr.is_valid()) {
if (scr.is_null()) {
EditorNode::get_singleton()->show_warning(TTR("Could not load file at:") + "\n\n" + path, TTR("Error!"));
} else {
int corrected_column = column;
@ -454,18 +456,19 @@ void ScriptTextEditor::convert_indent() {
void ScriptTextEditor::tag_saved_version() {
code_editor->get_text_editor()->tag_saved_version();
edited_file_data.last_modified_time = FileAccess::get_modified_time(edited_file_data.path);
}
void ScriptTextEditor::goto_line(int p_line, bool p_with_error) {
code_editor->goto_line(p_line);
void ScriptTextEditor::goto_line(int p_line, int p_column) {
code_editor->goto_line(p_line, p_column);
}
void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
code_editor->goto_line_selection(p_line, p_begin, p_end);
}
void ScriptTextEditor::goto_line_centered(int p_line) {
code_editor->goto_line_centered(p_line);
void ScriptTextEditor::goto_line_centered(int p_line, int p_column) {
code_editor->goto_line_centered(p_line, p_column);
}
void ScriptTextEditor::set_executing_line(int p_line) {
@ -577,7 +580,7 @@ void ScriptTextEditor::_update_warnings() {
bool has_connections_table = false;
// Add missing connections.
if (GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
if (GLOBAL_GET("debug/gdscript/warnings/enable")) {
Node *base = get_tree()->get_edited_scene_root();
if (base && missing_connections.size() > 0) {
has_connections_table = true;
@ -755,7 +758,7 @@ void ScriptTextEditor::_update_bookmark_list() {
line = line.substr(0, 50);
}
bookmarks_menu->add_item(String::num((int)bookmark_list[i] + 1) + " - `" + line + "`");
bookmarks_menu->add_item(String::num_int64(bookmark_list[i] + 1) + " - `" + line + "`");
bookmarks_menu->set_item_metadata(-1, bookmark_list[i]);
}
}
@ -853,7 +856,7 @@ void ScriptEditor::_update_modified_scripts_for_external_editor(Ref<Script> p_fo
if (last_date != date) {
Ref<Script> rel_scr = ResourceLoader::load(scr->get_path(), scr->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
ERR_CONTINUE(!rel_scr.is_valid());
ERR_CONTINUE(rel_scr.is_null());
scr->set_source_code(rel_scr->get_source_code());
scr->set_last_modified_time(rel_scr->get_last_modified_time());
scr->update_exports();
@ -910,7 +913,7 @@ void ScriptTextEditor::_update_breakpoint_list() {
line = line.substr(0, 50);
}
breakpoints_menu->add_item(String::num((int)breakpoint_list[i] + 1) + " - `" + line + "`");
breakpoints_menu->add_item(String::num_int64(breakpoint_list[i] + 1) + " - `" + line + "`");
breakpoints_menu->set_item_metadata(-1, breakpoint_list[i]);
}
}
@ -919,8 +922,7 @@ void ScriptTextEditor::_breakpoint_item_pressed(int p_idx) {
if (p_idx < 4) { // Any item before the separator.
_edit_option(breakpoints_menu->get_item_id(p_idx));
} else {
code_editor->goto_line(breakpoints_menu->get_item_metadata(p_idx));
callable_mp((TextEdit *)code_editor->get_text_editor(), &TextEdit::center_viewport_to_caret).call_deferred(0); // Needs to be deferred, because goto uses call_deferred().
code_editor->goto_line_centered(breakpoints_menu->get_item_metadata(p_idx));
}
}
@ -929,6 +931,9 @@ void ScriptTextEditor::_breakpoint_toggled(int p_row) {
}
void ScriptTextEditor::_on_caret_moved() {
if (code_editor->is_previewing_navigation_change()) {
return;
}
int current_line = code_editor->get_text_editor()->get_caret_line();
if (ABS(current_line - previous_line) >= 10) {
Dictionary nav_state = get_navigation_state();
@ -954,7 +959,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
} else if (p_symbol.is_resource_file() || p_symbol.begins_with("uid://")) {
String symbol = p_symbol;
if (symbol.begins_with("uid://")) {
symbol = ResourceUID::get_singleton()->get_id_path(ResourceUID::get_singleton()->text_to_id(symbol));
symbol = ResourceUID::uid_to_path(symbol);
}
List<String> scene_extensions;
@ -965,95 +970,88 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
} else {
EditorNode::get_singleton()->load_resource(symbol);
}
} else if (lc_error == OK) {
_goto_line(p_row);
switch (result.type) {
case ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION: {
if (result.script.is_valid()) {
emit_signal(SNAME("request_open_script_at_line"), result.script, result.location - 1);
} else {
emit_signal(SNAME("request_save_history"));
goto_line_centered(result.location - 1);
}
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS: {
emit_signal(SNAME("go_to_help"), "class_name:" + result.class_name);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT: {
StringName cname = result.class_name;
bool success;
while (true) {
ClassDB::get_integer_constant(cname, result.class_member, &success);
if (success) {
result.class_name = cname;
if (!result.class_name.is_empty() && EditorHelp::get_doc_data()->class_list.has(result.class_name) && !EditorHelp::get_doc_data()->class_list[result.class_name].is_script_doc) {
switch (result.type) {
case ScriptLanguage::LOOKUP_RESULT_CLASS: {
emit_signal(SNAME("go_to_help"), "class_name:" + result.class_name);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_integer_constant(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
} else {
break;
}
}
emit_signal(SNAME("go_to_help"), "class_constant:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY: {
emit_signal(SNAME("go_to_help"), "class_property:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD: {
StringName cname = result.class_name;
while (true) {
if (ClassDB::has_method(cname, result.class_member)) {
result.class_name = cname;
emit_signal(SNAME("go_to_help"), "class_constant:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_property(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
} else {
break;
}
}
emit_signal(SNAME("go_to_help"), "class_method:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL: {
StringName cname = result.class_name;
while (true) {
if (ClassDB::has_signal(cname, result.class_member)) {
result.class_name = cname;
emit_signal(SNAME("go_to_help"), "class_property:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_method(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
} else {
break;
}
}
emit_signal(SNAME("go_to_help"), "class_signal:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM: {
StringName cname = result.class_name;
StringName success;
while (true) {
success = ClassDB::get_integer_constant_enum(cname, result.class_member, true);
if (success != StringName()) {
result.class_name = cname;
emit_signal(SNAME("go_to_help"), "class_method:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_signal(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
} else {
break;
}
}
emit_signal(SNAME("go_to_help"), "class_enum:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION: {
emit_signal(SNAME("go_to_help"), "class_annotation:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE: {
emit_signal(SNAME("go_to_help"), "class_global:" + result.class_name + ":" + result.class_member);
} break;
default: {
emit_signal(SNAME("go_to_help"), "class_signal:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_enum(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
}
emit_signal(SNAME("go_to_help"), "class_enum:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION: {
emit_signal(SNAME("go_to_help"), "class_annotation:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE: { // Deprecated.
emit_signal(SNAME("go_to_help"), "class_global:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION:
case ScriptLanguage::LOOKUP_RESULT_LOCAL_CONSTANT:
case ScriptLanguage::LOOKUP_RESULT_LOCAL_VARIABLE:
case ScriptLanguage::LOOKUP_RESULT_MAX: {
// Nothing to do.
} break;
}
} else if (result.location >= 0) {
if (result.script.is_valid()) {
emit_signal(SNAME("request_open_script_at_line"), result.script, result.location - 1);
} else {
emit_signal(SNAME("request_save_history"));
goto_line_centered(result.location - 1);
}
}
} else if (ProjectSettings::get_singleton()->has_autoload(p_symbol)) {
@ -1104,6 +1102,126 @@ void ScriptTextEditor::_validate_symbol(const String &p_symbol) {
}
}
void ScriptTextEditor::_show_symbol_tooltip(const String &p_symbol, int p_row, int p_column) {
if (!EDITOR_GET("text_editor/behavior/documentation/enable_tooltips").booleanize()) {
return;
}
if (p_symbol.begins_with("res://") || p_symbol.begins_with("uid://")) {
EditorHelpBitTooltip::show_tooltip(code_editor->get_text_editor(), "resource||" + p_symbol);
return;
}
Node *base = get_tree()->get_edited_scene_root();
if (base) {
base = _find_node_for_script(base, base, script);
}
ScriptLanguage::LookupResult result;
String doc_symbol;
const String code_text = code_editor->get_text_editor()->get_text_with_cursor_char(p_row, p_column);
const Error lc_error = script->get_language()->lookup_code(code_text, p_symbol, script->get_path(), base, result);
if (lc_error == OK) {
switch (result.type) {
case ScriptLanguage::LOOKUP_RESULT_CLASS: {
doc_symbol = "class|" + result.class_name + "|";
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_integer_constant(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
}
doc_symbol = "constant|" + result.class_name + "|" + result.class_member;
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_property(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
}
doc_symbol = "property|" + result.class_name + "|" + result.class_member;
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_method(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
}
doc_symbol = "method|" + result.class_name + "|" + result.class_member;
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_signal(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
}
doc_symbol = "signal|" + result.class_name + "|" + result.class_member;
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM: {
StringName cname = result.class_name;
while (ClassDB::class_exists(cname)) {
if (ClassDB::has_enum(cname, result.class_member, true)) {
result.class_name = cname;
break;
}
cname = ClassDB::get_parent_class(cname);
}
doc_symbol = "enum|" + result.class_name + "|" + result.class_member;
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION: {
doc_symbol = "annotation|" + result.class_name + "|" + result.class_member;
} break;
case ScriptLanguage::LOOKUP_RESULT_LOCAL_CONSTANT:
case ScriptLanguage::LOOKUP_RESULT_LOCAL_VARIABLE: {
const String item_type = (result.type == ScriptLanguage::LOOKUP_RESULT_LOCAL_CONSTANT) ? "local_constant" : "local_variable";
Dictionary item_data;
item_data["description"] = result.description;
item_data["is_deprecated"] = result.is_deprecated;
item_data["deprecated_message"] = result.deprecated_message;
item_data["is_experimental"] = result.is_experimental;
item_data["experimental_message"] = result.experimental_message;
item_data["doc_type"] = result.doc_type;
item_data["enumeration"] = result.enumeration;
item_data["is_bitfield"] = result.is_bitfield;
item_data["value"] = result.value;
doc_symbol = item_type + "||" + p_symbol + "|" + JSON::stringify(item_data);
} break;
case ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION:
case ScriptLanguage::LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE: // Deprecated.
case ScriptLanguage::LOOKUP_RESULT_MAX: {
// Nothing to do.
} break;
}
}
// NOTE: See also `ScriptEditor::_get_debug_tooltip()` for documentation tooltips disabled.
String debug_value = EditorDebuggerNode::get_singleton()->get_var_value(p_symbol);
if (!debug_value.is_empty()) {
constexpr int DISPLAY_LIMIT = 1024;
if (debug_value.size() > DISPLAY_LIMIT) {
debug_value = debug_value.left(DISPLAY_LIMIT) + "... " + TTR("(truncated)");
}
debug_value = TTR("Current value: ") + debug_value.replace("[", "[lb]");
}
if (!doc_symbol.is_empty() || !debug_value.is_empty()) {
EditorHelpBitTooltip::show_tooltip(code_editor->get_text_editor(), doc_symbol, debug_value, true);
}
}
String ScriptTextEditor::_get_absolute_path(const String &rel_path) {
String base_path = script->get_path().get_base_dir();
String path = base_path.path_join(rel_path);
@ -1181,7 +1299,7 @@ void ScriptTextEditor::_update_connected_methods() {
// There is a chance that the method is inherited from another script.
bool found_inherited_function = false;
Ref<Script> inherited_script = script->get_base_script();
while (!inherited_script.is_null()) {
while (inherited_script.is_valid()) {
if (inherited_script->has_method(method)) {
found_inherited_function = true;
break;
@ -1208,7 +1326,7 @@ void ScriptTextEditor::_update_connected_methods() {
// Account for inner classes by stripping the class names from the method,
// starting from the right since our inner class might be inside of another inner class.
int pos = raw_name.rfind(".");
int pos = raw_name.rfind_char('.');
if (pos != -1) {
name = raw_name.substr(pos + 1);
}
@ -1216,7 +1334,7 @@ void ScriptTextEditor::_update_connected_methods() {
String found_base_class;
StringName base_class = script->get_instance_base_type();
Ref<Script> inherited_script = script->get_base_script();
while (!inherited_script.is_null()) {
while (inherited_script.is_valid()) {
if (inherited_script->has_method(name)) {
found_base_class = "script:" + inherited_script->get_path();
break;
@ -1312,7 +1430,7 @@ void ScriptTextEditor::_gutter_clicked(int p_line, int p_gutter) {
if (base_class_split[0] == "script") {
// Go to function declaration.
Ref<Script> base_script = ResourceLoader::load(base_class_split[1]);
ERR_FAIL_COND(!base_script.is_valid());
ERR_FAIL_COND(base_script.is_null());
emit_signal(SNAME("go_to_method"), base_script, method);
} else if (base_class_split[0] == "builtin") {
// Open method documentation.
@ -1521,7 +1639,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
quick_open->set_title(TTR("Go to Function"));
} break;
case SEARCH_GOTO_LINE: {
goto_line_dialog->popup_find_line(tx);
goto_line_popup->popup_find_line(code_editor);
} break;
case BOOKMARK_TOGGLE: {
code_editor->toggle_bookmark();
@ -1625,6 +1743,14 @@ void ScriptTextEditor::_edit_option(int p_op) {
_lookup_symbol(text, tx->get_caret_line(0), tx->get_caret_column(0));
}
} break;
case EDIT_EMOJI_AND_SYMBOL: {
code_editor->get_text_editor()->show_emoji_and_symbol_picker();
} break;
default: {
if (p_op >= EditorContextMenuPlugin::BASE_ID) {
EditorContextMenuPluginManager::get_singleton()->activate_custom_option(EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR_CODE, p_op, tx);
}
}
}
}
@ -1638,7 +1764,7 @@ void ScriptTextEditor::_edit_option_toggle_inline_comment() {
script->get_language()->get_comment_delimiters(&comment_delimiters);
for (const String &script_delimiter : comment_delimiters) {
if (!script_delimiter.contains(" ")) {
if (!script_delimiter.contains_char(' ')) {
delimiter = script_delimiter;
break;
}
@ -1816,9 +1942,9 @@ static String _get_dropped_resource_line(const Ref<Resource> &p_resource, bool p
}
if (is_script) {
variable_name = variable_name.to_pascal_case().validate_identifier();
variable_name = variable_name.to_pascal_case().validate_unicode_identifier();
} else {
variable_name = variable_name.to_snake_case().to_upper().validate_identifier();
variable_name = variable_name.to_snake_case().to_upper().validate_unicode_identifier();
}
return vformat("const %s = preload(%s)", variable_name, _quote_drop_data(path));
}
@ -1846,7 +1972,8 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
const String &line = te->get_line(drop_at_line);
const bool is_empty_line = line_will_be_empty || line.is_empty() || te->get_first_non_whitespace_column(drop_at_line) == line.length();
if (d.has("type") && String(d["type"]) == "resource") {
const String type = d.get("type", "");
if (type == "resource") {
Ref<Resource> resource = d["resource"];
if (resource.is_null()) {
return;
@ -1871,11 +1998,11 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
}
}
if (d.has("type") && (String(d["type"]) == "files" || String(d["type"]) == "files_and_dirs")) {
Array files = d["files"];
for (int i = 0; i < files.size(); i++) {
const String &path = String(files[i]);
if (type == "files" || type == "files_and_dirs") {
const PackedStringArray files = d["files"];
PackedStringArray parts;
for (const String &path : files) {
if (drop_modifier_pressed && ResourceLoader::exists(path)) {
Ref<Resource> resource = ResourceLoader::load(path);
if (resource.is_null()) {
@ -1883,18 +2010,15 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
resource.instantiate();
resource->set_path_cache(path);
}
text_to_drop += _get_dropped_resource_line(resource, is_empty_line);
parts.append(_get_dropped_resource_line(resource, is_empty_line));
} else {
text_to_drop += _quote_drop_data(path);
}
if (i < files.size() - 1) {
text_to_drop += is_empty_line ? "\n" : ", ";
parts.append(_quote_drop_data(path));
}
}
text_to_drop = String(is_empty_line ? "\n" : ", ").join(parts);
}
if (d.has("type") && String(d["type"]) == "nodes") {
if (type == "nodes") {
Node *scene_root = get_tree()->get_edited_scene_root();
if (!scene_root) {
EditorNode::get_singleton()->show_warning(TTR("Can't drop nodes without an open scene."));
@ -1932,13 +2056,13 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
path = sn->get_path_to(node);
}
for (const String &segment : path.split("/")) {
if (!segment.is_valid_identifier()) {
if (!segment.is_valid_unicode_identifier()) {
path = _quote_drop_data(path);
break;
}
}
String variable_name = String(node->get_name()).to_snake_case().validate_identifier();
String variable_name = String(node->get_name()).to_snake_case().validate_unicode_identifier();
if (use_type) {
StringName class_name = node->get_class_name();
Ref<Script> node_script = node->get_script();
@ -1975,7 +2099,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
}
for (const String &segment : path.split("/")) {
if (!segment.is_valid_identifier()) {
if (!segment.is_valid_ascii_identifier()) {
path = _quote_drop_data(path);
break;
}
@ -1985,7 +2109,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
}
}
if (d.has("type") && String(d["type"]) == "obj_property") {
if (type == "obj_property") {
bool add_literal = EDITOR_GET("text_editor/completion/add_node_path_literals");
text_to_drop = add_literal ? "^" : "";
// It is unclear whether properties may contain single or double quotes.
@ -2178,6 +2302,10 @@ void ScriptTextEditor::_prepare_edit_menu() {
void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition, Vector2 p_pos) {
context_menu->clear();
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EMOJI_AND_SYMBOL_PICKER)) {
context_menu->add_item(TTR("Emoji & Symbols"), EDIT_EMOJI_AND_SYMBOL);
context_menu->add_separator();
}
context_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO);
context_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO);
@ -2209,13 +2337,16 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p
if (p_color || p_open_docs || p_goto_definition) {
context_menu->add_separator();
if (p_open_docs) {
context_menu->add_item(TTR("Lookup Symbol"), LOOKUP_SYMBOL);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_symbol"), LOOKUP_SYMBOL);
}
if (p_color) {
context_menu->add_item(TTR("Pick Color"), EDIT_PICK_COLOR);
}
}
const PackedStringArray paths = { code_editor->get_text_editor()->get_path() };
EditorContextMenuPluginManager::get_singleton()->add_options_from_plugins(context_menu, EditorContextMenuPlugin::CONTEXT_SLOT_SCRIPT_EDITOR_CODE, paths);
const CodeEdit *tx = code_editor->get_text_editor();
context_menu->set_item_disabled(context_menu->get_item_index(EDIT_UNDO), !tx->has_undo());
context_menu->set_item_disabled(context_menu->get_item_index(EDIT_REDO), !tx->has_redo());
@ -2239,6 +2370,7 @@ void ScriptTextEditor::_enable_code_editor() {
code_editor->connect("validate_script", callable_mp(this, &ScriptTextEditor::_validate_script));
code_editor->connect("load_theme_settings", callable_mp(this, &ScriptTextEditor::_load_theme_settings));
code_editor->get_text_editor()->connect("symbol_lookup", callable_mp(this, &ScriptTextEditor::_lookup_symbol));
code_editor->get_text_editor()->connect("symbol_hovered", callable_mp(this, &ScriptTextEditor::_show_symbol_tooltip));
code_editor->get_text_editor()->connect("symbol_validate", callable_mp(this, &ScriptTextEditor::_validate_symbol));
code_editor->get_text_editor()->connect("gutter_added", callable_mp(this, &ScriptTextEditor::_update_gutter_indexes));
code_editor->get_text_editor()->connect("gutter_removed", callable_mp(this, &ScriptTextEditor::_update_gutter_indexes));
@ -2277,8 +2409,8 @@ void ScriptTextEditor::_enable_code_editor() {
quick_open->connect("goto_line", callable_mp(this, &ScriptTextEditor::_goto_line));
add_child(quick_open);
goto_line_dialog = memnew(GotoLineDialog);
add_child(goto_line_dialog);
goto_line_popup = memnew(GotoLinePopup);
add_child(goto_line_popup);
add_child(connection_info_dialog);
@ -2359,6 +2491,7 @@ void ScriptTextEditor::_enable_code_editor() {
edit_hb->add_child(goto_menu);
goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_function"), SEARCH_LOCATE_FUNCTION);
goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
goto_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_symbol"), LOOKUP_SYMBOL);
goto_menu->get_popup()->add_separator();
goto_menu->get_popup()->add_submenu_node_item(TTR("Bookmarks"), bookmarks_menu);
@ -2376,6 +2509,7 @@ void ScriptTextEditor::_enable_code_editor() {
ScriptTextEditor::ScriptTextEditor() {
code_editor = memnew(CodeTextEditor);
code_editor->set_toggle_list_control(ScriptEditor::get_singleton()->get_left_list_split());
code_editor->add_theme_constant_override("separation", 2);
code_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
code_editor->set_code_complete_func(_code_complete_scripts, this);
@ -2385,6 +2519,7 @@ ScriptTextEditor::ScriptTextEditor() {
code_editor->get_text_editor()->set_draw_executing_lines_gutter(true);
code_editor->get_text_editor()->connect("breakpoint_toggled", callable_mp(this, &ScriptTextEditor::_breakpoint_toggled));
code_editor->get_text_editor()->connect("caret_changed", callable_mp(this, &ScriptTextEditor::_on_caret_moved));
code_editor->connect("navigation_preview_ended", callable_mp(this, &ScriptTextEditor::_on_caret_moved));
connection_gutter = 1;
code_editor->get_text_editor()->add_gutter(connection_gutter);
@ -2414,6 +2549,7 @@ ScriptTextEditor::ScriptTextEditor() {
update_settings();
code_editor->get_text_editor()->set_symbol_lookup_on_click_enabled(true);
code_editor->get_text_editor()->set_symbol_tooltip_on_hover_enabled(true);
code_editor->get_text_editor()->set_context_menu_enabled(false);
context_menu = memnew(PopupMenu);
@ -2484,69 +2620,71 @@ static ScriptEditorBase *create_editor(const Ref<Resource> &p_resource) {
}
void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/move_up", TTR("Move Up"), KeyModifierMask::ALT | Key::UP);
ED_SHORTCUT("script_text_editor/move_down", TTR("Move Down"), KeyModifierMask::ALT | Key::DOWN);
ED_SHORTCUT("script_text_editor/delete_line", TTR("Delete Line"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::K);
ED_SHORTCUT("script_text_editor/move_up", TTRC("Move Up"), KeyModifierMask::ALT | Key::UP);
ED_SHORTCUT("script_text_editor/move_down", TTRC("Move Down"), KeyModifierMask::ALT | Key::DOWN);
ED_SHORTCUT("script_text_editor/delete_line", TTRC("Delete Line"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::K);
// Leave these at zero, same can be accomplished with tab/shift-tab, including selection.
// The next/previous in history shortcut in this case makes a lot more sense.
ED_SHORTCUT("script_text_editor/indent", TTR("Indent"), Key::NONE);
ED_SHORTCUT("script_text_editor/unindent", TTR("Unindent"), KeyModifierMask::SHIFT | Key::TAB);
ED_SHORTCUT_ARRAY("script_text_editor/toggle_comment", TTR("Toggle Comment"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::K), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::SLASH), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_DIVIDE), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::NUMBERSIGN) });
ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KeyModifierMask::ALT | Key::F);
ED_SHORTCUT("script_text_editor/indent", TTRC("Indent"), Key::NONE);
ED_SHORTCUT("script_text_editor/unindent", TTRC("Unindent"), KeyModifierMask::SHIFT | Key::TAB);
ED_SHORTCUT_ARRAY("script_text_editor/toggle_comment", TTRC("Toggle Comment"), { int32_t(KeyModifierMask::CMD_OR_CTRL | Key::K), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::SLASH), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::KP_DIVIDE), int32_t(KeyModifierMask::CMD_OR_CTRL | Key::NUMBERSIGN) });
ED_SHORTCUT("script_text_editor/toggle_fold_line", TTRC("Fold/Unfold Line"), KeyModifierMask::ALT | Key::F);
ED_SHORTCUT_OVERRIDE("script_text_editor/toggle_fold_line", "macos", KeyModifierMask::CTRL | KeyModifierMask::META | Key::F);
ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), Key::NONE);
ED_SHORTCUT("script_text_editor/create_code_region", TTR("Create Code Region"), KeyModifierMask::ALT | Key::R);
ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), Key::NONE);
ED_SHORTCUT("script_text_editor/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::SHIFT | KeyModifierMask::CTRL | Key::D);
ED_SHORTCUT("script_text_editor/fold_all_lines", TTRC("Fold All Lines"), Key::NONE);
ED_SHORTCUT("script_text_editor/create_code_region", TTRC("Create Code Region"), KeyModifierMask::ALT | Key::R);
ED_SHORTCUT("script_text_editor/unfold_all_lines", TTRC("Unfold All Lines"), Key::NONE);
ED_SHORTCUT("script_text_editor/duplicate_selection", TTRC("Duplicate Selection"), KeyModifierMask::SHIFT | KeyModifierMask::CTRL | Key::D);
ED_SHORTCUT_OVERRIDE("script_text_editor/duplicate_selection", "macos", KeyModifierMask::SHIFT | KeyModifierMask::META | Key::C);
ED_SHORTCUT("script_text_editor/duplicate_lines", TTR("Duplicate Lines"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::DOWN);
ED_SHORTCUT("script_text_editor/duplicate_lines", TTRC("Duplicate Lines"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::DOWN);
ED_SHORTCUT_OVERRIDE("script_text_editor/duplicate_lines", "macos", KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::DOWN);
ED_SHORTCUT("script_text_editor/evaluate_selection", TTR("Evaluate Selection"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::E);
ED_SHORTCUT("script_text_editor/toggle_word_wrap", TTR("Toggle Word Wrap"), KeyModifierMask::ALT | Key::Z);
ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::T);
ED_SHORTCUT("script_text_editor/trim_final_newlines", TTR("Trim Final Newlines"), Key::NONE);
ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent to Spaces"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::Y);
ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent to Tabs"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::I);
ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KeyModifierMask::CMD_OR_CTRL | Key::I);
ED_SHORTCUT("script_text_editor/evaluate_selection", TTRC("Evaluate Selection"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::E);
ED_SHORTCUT("script_text_editor/toggle_word_wrap", TTRC("Toggle Word Wrap"), KeyModifierMask::ALT | Key::Z);
ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTRC("Trim Trailing Whitespace"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::T);
ED_SHORTCUT("script_text_editor/trim_final_newlines", TTRC("Trim Final Newlines"), Key::NONE);
ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTRC("Convert Indent to Spaces"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::Y);
ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTRC("Convert Indent to Tabs"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::I);
ED_SHORTCUT("script_text_editor/auto_indent", TTRC("Auto Indent"), KeyModifierMask::CMD_OR_CTRL | Key::I);
ED_SHORTCUT_AND_COMMAND("script_text_editor/find", TTR("Find..."), KeyModifierMask::CMD_OR_CTRL | Key::F);
ED_SHORTCUT_AND_COMMAND("script_text_editor/find", TTRC("Find..."), KeyModifierMask::CMD_OR_CTRL | Key::F);
ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), Key::F3);
ED_SHORTCUT("script_text_editor/find_next", TTRC("Find Next"), Key::F3);
ED_SHORTCUT_OVERRIDE("script_text_editor/find_next", "macos", KeyModifierMask::META | Key::G);
ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KeyModifierMask::SHIFT | Key::F3);
ED_SHORTCUT("script_text_editor/find_previous", TTRC("Find Previous"), KeyModifierMask::SHIFT | Key::F3);
ED_SHORTCUT_OVERRIDE("script_text_editor/find_previous", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::G);
ED_SHORTCUT_AND_COMMAND("script_text_editor/replace", TTR("Replace..."), KeyModifierMask::CTRL | Key::R);
ED_SHORTCUT_AND_COMMAND("script_text_editor/replace", TTRC("Replace..."), KeyModifierMask::CTRL | Key::R);
ED_SHORTCUT_OVERRIDE("script_text_editor/replace", "macos", KeyModifierMask::ALT | KeyModifierMask::META | Key::F);
ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in Files..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F);
ED_SHORTCUT("script_text_editor/replace_in_files", TTR("Replace in Files..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R);
ED_SHORTCUT("script_text_editor/find_in_files", TTRC("Find in Files..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F);
ED_SHORTCUT("script_text_editor/replace_in_files", TTRC("Replace in Files..."), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::R);
ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KeyModifierMask::ALT | Key::F1);
ED_SHORTCUT("script_text_editor/contextual_help", TTRC("Contextual Help"), KeyModifierMask::ALT | Key::F1);
ED_SHORTCUT_OVERRIDE("script_text_editor/contextual_help", "macos", KeyModifierMask::ALT | KeyModifierMask::SHIFT | Key::SPACE);
ED_SHORTCUT("script_text_editor/toggle_bookmark", TTR("Toggle Bookmark"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::B);
ED_SHORTCUT("script_text_editor/toggle_bookmark", TTRC("Toggle Bookmark"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::B);
ED_SHORTCUT("script_text_editor/goto_next_bookmark", TTR("Go to Next Bookmark"), KeyModifierMask::CMD_OR_CTRL | Key::B);
ED_SHORTCUT("script_text_editor/goto_next_bookmark", TTRC("Go to Next Bookmark"), KeyModifierMask::CMD_OR_CTRL | Key::B);
ED_SHORTCUT_OVERRIDE("script_text_editor/goto_next_bookmark", "macos", KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::B);
ED_SHORTCUT("script_text_editor/goto_previous_bookmark", TTR("Go to Previous Bookmark"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::B);
ED_SHORTCUT("script_text_editor/remove_all_bookmarks", TTR("Remove All Bookmarks"), Key::NONE);
ED_SHORTCUT("script_text_editor/goto_previous_bookmark", TTRC("Go to Previous Bookmark"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::B);
ED_SHORTCUT("script_text_editor/remove_all_bookmarks", TTRC("Remove All Bookmarks"), Key::NONE);
ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KeyModifierMask::ALT | KeyModifierMask::CTRL | Key::F);
ED_SHORTCUT("script_text_editor/goto_function", TTRC("Go to Function..."), KeyModifierMask::ALT | KeyModifierMask::CTRL | Key::F);
ED_SHORTCUT_OVERRIDE("script_text_editor/goto_function", "macos", KeyModifierMask::CTRL | KeyModifierMask::META | Key::J);
ED_SHORTCUT("script_text_editor/goto_line", TTR("Go to Line..."), KeyModifierMask::CMD_OR_CTRL | Key::L);
ED_SHORTCUT("script_text_editor/goto_line", TTRC("Go to Line..."), KeyModifierMask::CMD_OR_CTRL | Key::L);
ED_SHORTCUT("script_text_editor/goto_symbol", TTRC("Lookup Symbol"));
ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), Key::F9);
ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTRC("Toggle Breakpoint"), Key::F9);
ED_SHORTCUT_OVERRIDE("script_text_editor/toggle_breakpoint", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::B);
ED_SHORTCUT("script_text_editor/remove_all_breakpoints", TTR("Remove All Breakpoints"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F9);
ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Go to Next Breakpoint"), KeyModifierMask::CMD_OR_CTRL | Key::PERIOD);
ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Go to Previous Breakpoint"), KeyModifierMask::CMD_OR_CTRL | Key::COMMA);
ED_SHORTCUT("script_text_editor/remove_all_breakpoints", TTRC("Remove All Breakpoints"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::F9);
// Using Control for these shortcuts even on macOS because Command+Comma is taken for opening Editor Settings.
ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTRC("Go to Next Breakpoint"), KeyModifierMask::CTRL | Key::PERIOD);
ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTRC("Go to Previous Breakpoint"), KeyModifierMask::CTRL | Key::COMMA);
ScriptEditor::register_create_script_editor_function(create_editor);
}