feat: modules moved and engine moved to submodule

This commit is contained in:
Jan van der Weide 2025-04-12 18:40:44 +02:00
parent dfb5e645cd
commit c33d2130cc
5136 changed files with 225275 additions and 64485 deletions

View file

@ -144,9 +144,7 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const
// Send all current breakpoints
List<String> breakpoints;
ScriptEditor::get_singleton()->get_breakpoints(&breakpoints);
for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) {
String breakpoint = E->get();
for (const String &breakpoint : breakpoints) {
String path = breakpoint.left(breakpoint.find_char(':', 6)); // Skip initial part of path, aka "res://"
int line = breakpoint.substr(path.size()).to_int();
@ -301,12 +299,11 @@ Dictionary DebugAdapterParser::req_threads(const Dictionary &p_params) const {
Dictionary response = prepare_success_response(p_params), body;
response["body"] = body;
Array arr;
DAP::Thread thread;
thread.id = 1; // Hardcoded because Godot only supports debugging one thread at the moment
thread.name = "Main";
arr.push_back(thread.to_json());
Array arr = { thread.to_json() };
body["threads"] = arr;
return response;
@ -325,8 +322,7 @@ Dictionary DebugAdapterParser::req_stackTrace(const Dictionary &p_params) const
Array arr;
DebugAdapterProtocol *dap = DebugAdapterProtocol::get_singleton();
for (const KeyValue<DAP::StackFrame, List<int>> &E : dap->stackframe_list) {
DAP::StackFrame sf = E.key;
for (DAP::StackFrame sf : dap->stackframe_list) {
if (!lines_at_one) {
sf.line--;
}
@ -360,7 +356,7 @@ Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) co
// If path contains \, it's a Windows path, so we need to convert it to /, and make the drive letter uppercase
if (source.path.contains_char('\\')) {
source.path = source.path.replace("\\", "/");
source.path = source.path.replace_char('\\', '/');
source.path = source.path.substr(0, 1).to_upper() + source.path.substr(1);
}
@ -372,6 +368,8 @@ Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) co
lines.push_back(breakpoint.line + !lines_at_one);
}
// Always update the source checksum for the requested path, as it might have been modified externally.
DebugAdapterProtocol::get_singleton()->update_source(source.path);
Array updated_breakpoints = DebugAdapterProtocol::get_singleton()->update_breakpoints(source.path, lines);
body["breakpoints"] = updated_breakpoints;
@ -383,13 +381,12 @@ Dictionary DebugAdapterParser::req_breakpointLocations(const Dictionary &p_param
response["body"] = body;
Dictionary args = p_params["arguments"];
Array locations;
DAP::BreakpointLocation location;
location.line = args["line"];
if (args.has("endLine")) {
location.endLine = args["endLine"];
}
locations.push_back(location.to_json());
Array locations = { location.to_json() };
body["breakpoints"] = locations;
return response;
@ -403,15 +400,13 @@ Dictionary DebugAdapterParser::req_scopes(const Dictionary &p_params) const {
int frame_id = args["frameId"];
Array scope_list;
DAP::StackFrame frame;
frame.id = frame_id;
HashMap<DAP::StackFrame, List<int>, DAP::StackFrame>::Iterator E = DebugAdapterProtocol::get_singleton()->stackframe_list.find(frame);
HashMap<DebugAdapterProtocol::DAPStackFrameID, Vector<int>>::Iterator E = DebugAdapterProtocol::get_singleton()->scope_list.find(frame_id);
if (E) {
ERR_FAIL_COND_V(E->value.size() != 3, prepare_error_response(p_params, DAP::ErrorType::UNKNOWN));
List<int>::ConstIterator itr = E->value.begin();
for (int i = 0; i < 3; ++itr, ++i) {
const Vector<int> &scope_ids = E->value;
ERR_FAIL_COND_V(scope_ids.size() != 3, prepare_error_response(p_params, DAP::ErrorType::UNKNOWN));
for (int i = 0; i < 3; ++i) {
DAP::Scope scope;
scope.variablesReference = *itr;
scope.variablesReference = scope_ids[i];
switch (i) {
case 0:
scope.name = "Locals";
@ -595,8 +590,7 @@ Dictionary DebugAdapterParser::ev_stopped_breakpoint(const int &p_id) const {
body["reason"] = "breakpoint";
body["description"] = "Breakpoint";
Array breakpoints;
breakpoints.push_back(p_id);
Array breakpoints = { p_id };
body["hitBreakpointIds"] = breakpoints;
return event;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DEBUG_ADAPTER_PARSER_H
#define DEBUG_ADAPTER_PARSER_H
#pragma once
#include "core/config/project_settings.h"
#include "core/debugger/remote_debugger.h"
@ -49,7 +48,7 @@ private:
// If path contains \, it's a Windows path, so we need to convert it to /, and check as case-insensitive.
if (p_path.contains_char('\\')) {
String project_path = ProjectSettings::get_singleton()->get_resource_path();
String path = p_path.replace("\\", "/");
String path = p_path.replace_char('\\', '/');
return path.containsn(project_path);
}
return p_path.begins_with(ProjectSettings::get_singleton()->get_resource_path());
@ -103,5 +102,3 @@ public:
Dictionary ev_custom_data(const String &p_msg, const Array &p_data) const;
Dictionary ev_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) const;
};
#endif // DEBUG_ADAPTER_PARSER_H

View file

@ -66,8 +66,7 @@ Error DAPeer::handle_data() {
// End of headers
if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
r[l - 3] = '\0'; // Null terminate to read string
String header;
header.parse_utf8(r);
String header = String::utf8(r);
content_length = header.substr(16).to_int();
has_header = true;
req_pos = 0;
@ -93,8 +92,7 @@ Error DAPeer::handle_data() {
}
// Parse data
String msg;
msg.parse_utf8((const char *)req_buf, req_pos);
String msg = String::utf8((const char *)req_buf, req_pos);
// Apply a timestamp if it there's none yet
if (!timestamp) {
@ -118,12 +116,12 @@ Error DAPeer::send_data() {
if (!data.has("seq")) {
data["seq"] = ++seq;
}
String formatted_data = format_output(data);
const Vector<uint8_t> &formatted_data = format_output(data);
int data_sent = 0;
while (data_sent < formatted_data.length()) {
while (data_sent < formatted_data.size()) {
int curr_sent = 0;
Error err = connection->put_partial_data((const uint8_t *)formatted_data.utf8().get_data(), formatted_data.size() - data_sent - 1, curr_sent);
Error err = connection->put_partial_data(formatted_data.ptr() + data_sent, formatted_data.size() - data_sent, curr_sent);
if (err != OK) {
return err;
}
@ -134,15 +132,12 @@ Error DAPeer::send_data() {
return OK;
}
String DAPeer::format_output(const Dictionary &p_params) const {
String response = Variant(p_params).to_json_string();
String header = "Content-Length: ";
CharString charstr = response.utf8();
size_t len = charstr.length();
header += itos(len);
header += "\r\n\r\n";
Vector<uint8_t> DAPeer::format_output(const Dictionary &p_params) const {
const Vector<uint8_t> &content = Variant(p_params).to_json_string().to_utf8_buffer();
Vector<uint8_t> response = vformat("Content-Length: %d\r\n\r\n", content.size()).to_utf8_buffer();
return header + response;
response.append_array(content);
return response;
}
Error DebugAdapterProtocol::on_client_connected() {
@ -176,6 +171,7 @@ void DebugAdapterProtocol::reset_current_info() {
void DebugAdapterProtocol::reset_ids() {
breakpoint_id = 0;
breakpoint_list.clear();
breakpoint_source_list.clear();
reset_stack_info();
}
@ -185,6 +181,7 @@ void DebugAdapterProtocol::reset_stack_info() {
variable_id = 1;
stackframe_list.clear();
scope_list.clear();
variable_list.clear();
object_list.clear();
object_pending_set.clear();
@ -205,9 +202,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
x.value = rtos(vec.x);
y.value = rtos(vec.y);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
Array arr = { x.to_json(), y.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -230,11 +225,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
w.value = rtos(rect.size.x);
h.value = rtos(rect.size.y);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(w.to_json());
arr.push_back(h.to_json());
Array arr = { x.to_json(), y.to_json(), w.to_json(), h.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -254,10 +245,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
y.value = rtos(vec.y);
z.value = rtos(vec.z);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(z.to_json());
Array arr = { x.to_json(), y.to_json(), z.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -279,10 +267,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
y.variablesReference = parse_variant(transform.columns[1]);
origin.variablesReference = parse_variant(transform.columns[2]);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(origin.to_json());
Array arr = { x.to_json(), y.to_json(), origin.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -298,9 +283,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
normal.value = plane.normal;
normal.variablesReference = parse_variant(plane.normal);
Array arr;
arr.push_back(d.to_json());
arr.push_back(normal.to_json());
Array arr = { d.to_json(), normal.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -322,11 +305,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
z.value = rtos(quat.z);
w.value = rtos(quat.w);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(z.to_json());
arr.push_back(w.to_json());
Array arr = { x.to_json(), y.to_json(), z.to_json(), w.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -344,9 +323,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
position.variablesReference = parse_variant(aabb.position);
size.variablesReference = parse_variant(aabb.size);
Array arr;
arr.push_back(position.to_json());
arr.push_back(size.to_json());
Array arr = { position.to_json(), size.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -368,10 +345,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
y.variablesReference = parse_variant(basis.rows[1]);
z.variablesReference = parse_variant(basis.rows[2]);
Array arr;
arr.push_back(x.to_json());
arr.push_back(y.to_json());
arr.push_back(z.to_json());
Array arr = { x.to_json(), y.to_json(), z.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -388,9 +362,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
basis.variablesReference = parse_variant(transform.basis);
origin.variablesReference = parse_variant(transform.origin);
Array arr;
arr.push_back(basis.to_json());
arr.push_back(origin.to_json());
Array arr = { basis.to_json(), origin.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -412,11 +384,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
b.value = rtos(color.b);
a.value = rtos(color.a);
Array arr;
arr.push_back(r.to_json());
arr.push_back(g.to_json());
arr.push_back(b.to_json());
arr.push_back(a.to_json());
Array arr = { r.to_json(), g.to_json(), b.to_json(), a.to_json() };
variable_list.insert(id, arr);
return id;
}
@ -428,8 +396,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -447,10 +414,10 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
Dictionary dictionary = p_var;
Array arr;
for (int i = 0; i < dictionary.size(); i++) {
for (const KeyValue<Variant, Variant> &kv : dictionary) {
DAP::Variable var;
var.name = dictionary.get_key_at_index(i);
Variant value = dictionary.get_value_at_index(i);
var.name = kv.key;
Variant value = kv.value;
var.type = Variant::get_type_name(value.get_type());
var.value = value;
var.variablesReference = parse_variant(value);
@ -467,8 +434,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -488,8 +454,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -509,8 +474,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -530,8 +494,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -551,8 +514,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -572,8 +534,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -593,8 +554,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -615,8 +575,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -637,8 +596,7 @@ int DebugAdapterProtocol::parse_variant(const Variant &p_var) {
size.type = Variant::get_type_name(Variant::INT);
size.value = itos(array.size());
Array arr;
arr.push_back(size.to_json());
Array arr = { size.to_json() };
for (int i = 0; i < array.size(); i++) {
DAP::Variable var;
@ -843,7 +801,9 @@ bool DebugAdapterProtocol::request_remote_object(const ObjectID &p_object_id) {
return false;
}
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_object(p_object_id);
TypedArray<uint64_t> arr;
arr.append(p_object_id);
EditorDebuggerNode::get_singleton()->get_default_debugger()->request_remote_objects(arr);
object_pending_set.insert(p_object_id);
return true;
@ -861,6 +821,30 @@ bool DebugAdapterProtocol::request_remote_evaluate(const String &p_eval, int p_s
return true;
}
const DAP::Source &DebugAdapterProtocol::fetch_source(const String &p_path) {
const String &global_path = ProjectSettings::get_singleton()->globalize_path(p_path);
HashMap<String, DAP::Source>::Iterator E = breakpoint_source_list.find(global_path);
if (E != breakpoint_source_list.end()) {
return E->value;
}
DAP::Source &added_source = breakpoint_source_list.insert(global_path, DAP::Source())->value;
added_source.name = global_path.get_file();
added_source.path = global_path;
added_source.compute_checksums();
return added_source;
}
void DebugAdapterProtocol::update_source(const String &p_path) {
const String &global_path = ProjectSettings::get_singleton()->globalize_path(p_path);
HashMap<String, DAP::Source>::Iterator E = breakpoint_source_list.find(global_path);
if (E != breakpoint_source_list.end()) {
E->value.compute_checksums();
}
}
bool DebugAdapterProtocol::process_message(const String &p_text) {
JSON json;
ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Malformed message!");
@ -878,8 +862,7 @@ bool DebugAdapterProtocol::process_message(const String &p_text) {
if (parser->has_method(command)) {
_current_request = params["command"];
Array args;
args.push_back(params);
Array args = { params };
Dictionary response = parser->callv(command, args);
if (!response.is_empty()) {
_current_peer->res_queue.push_front(response);
@ -904,66 +887,66 @@ void DebugAdapterProtocol::notify_process() {
String launch_mode = _current_peer->attached ? "attach" : "launch";
Dictionary event = parser->ev_process(launch_mode);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_terminated() {
Dictionary event = parser->ev_terminated();
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
if ((_current_request == "launch" || _current_request == "restart") && _current_peer == E->get()) {
for (const Ref<DAPeer> &peer : clients) {
if ((_current_request == "launch" || _current_request == "restart") && _current_peer == peer) {
continue;
}
E->get()->res_queue.push_back(event);
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_exited(const int &p_exitcode) {
Dictionary event = parser->ev_exited(p_exitcode);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
if ((_current_request == "launch" || _current_request == "restart") && _current_peer == E->get()) {
for (const Ref<DAPeer> &peer : clients) {
if ((_current_request == "launch" || _current_request == "restart") && _current_peer == peer) {
continue;
}
E->get()->res_queue.push_back(event);
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_stopped_paused() {
Dictionary event = parser->ev_stopped_paused();
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_stopped_exception(const String &p_error) {
Dictionary event = parser->ev_stopped_exception(p_error);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_stopped_breakpoint(const int &p_id) {
Dictionary event = parser->ev_stopped_breakpoint(p_id);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_stopped_step() {
Dictionary event = parser->ev_stopped_step();
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_continued() {
Dictionary event = parser->ev_continued();
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
if (_current_request == "continue" && E->get() == _current_peer) {
for (const Ref<DAPeer> &peer : clients) {
if (_current_request == "continue" && peer == _current_peer) {
continue;
}
E->get()->res_queue.push_back(event);
peer->res_queue.push_back(event);
}
reset_stack_info();
@ -971,15 +954,14 @@ void DebugAdapterProtocol::notify_continued() {
void DebugAdapterProtocol::notify_output(const String &p_message, RemoteDebugger::MessageType p_type) {
Dictionary event = parser->ev_output(p_message, p_type);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->res_queue.push_back(event);
for (const Ref<DAPeer> &peer : clients) {
peer->res_queue.push_back(event);
}
}
void DebugAdapterProtocol::notify_custom_data(const String &p_msg, const Array &p_data) {
Dictionary event = parser->ev_custom_data(p_msg, p_data);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
Ref<DAPeer> peer = E->get();
for (const Ref<DAPeer> &peer : clients) {
if (peer->supportsCustomData) {
peer->res_queue.push_back(event);
}
@ -988,11 +970,11 @@ void DebugAdapterProtocol::notify_custom_data(const String &p_msg, const Array &
void DebugAdapterProtocol::notify_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) {
Dictionary event = parser->ev_breakpoint(p_breakpoint, p_enabled);
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
if (_current_request == "setBreakpoints" && E->get() == _current_peer) {
for (const Ref<DAPeer> &peer : clients) {
if (_current_request == "setBreakpoints" && peer == _current_peer) {
continue;
}
E->get()->res_queue.push_back(event);
peer->res_queue.push_back(event);
}
}
@ -1001,23 +983,40 @@ Array DebugAdapterProtocol::update_breakpoints(const String &p_path, const Array
// Add breakpoints
for (int i = 0; i < p_lines.size(); i++) {
EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, p_lines[i], true);
DAP::Breakpoint breakpoint;
DAP::Breakpoint breakpoint(fetch_source(p_path));
breakpoint.line = p_lines[i];
breakpoint.source.path = p_path;
ERR_FAIL_COND_V(!breakpoint_list.find(breakpoint), Array());
updated_breakpoints.push_back(breakpoint_list.find(breakpoint)->get().to_json());
// Avoid duplicated entries.
List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint);
if (E) {
updated_breakpoints.push_back(E->get().to_json());
continue;
}
EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, p_lines[i], true);
// Breakpoints are inserted at the end of the breakpoint list.
List<DAP::Breakpoint>::Element *added_breakpoint = breakpoint_list.back();
ERR_FAIL_NULL_V(added_breakpoint, Array());
ERR_FAIL_COND_V(!(added_breakpoint->get() == breakpoint), Array());
updated_breakpoints.push_back(added_breakpoint->get().to_json());
}
// Remove breakpoints
for (List<DAP::Breakpoint>::Element *E = breakpoint_list.front(); E; E = E->next()) {
DAP::Breakpoint b = E->get();
if (b.source.path == p_path && !p_lines.has(b.line)) {
EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, b.line, false);
// Must be deferred because we are iterating the breakpoint list.
Vector<int> to_remove;
for (const DAP::Breakpoint &b : breakpoint_list) {
if (b.source->path == p_path && !p_lines.has(b.line)) {
to_remove.push_back(b.line);
}
}
// Safe to remove queued data now.
for (const int &line : to_remove) {
EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, line, false);
}
return updated_breakpoints;
}
@ -1032,6 +1031,7 @@ void DebugAdapterProtocol::on_debug_paused() {
void DebugAdapterProtocol::on_debug_stopped() {
notify_exited();
notify_terminated();
reset_ids();
}
void DebugAdapterProtocol::on_debug_output(const String &p_message, int p_type) {
@ -1059,10 +1059,8 @@ void DebugAdapterProtocol::on_debug_breaked(const bool &p_reallydid, const bool
}
void DebugAdapterProtocol::on_debug_breakpoint_toggled(const String &p_path, const int &p_line, const bool &p_enabled) {
DAP::Breakpoint breakpoint;
DAP::Breakpoint breakpoint(fetch_source(p_path));
breakpoint.verified = true;
breakpoint.source.path = ProjectSettings::get_singleton()->globalize_path(p_path);
breakpoint.source.compute_checksums();
breakpoint.line = p_line;
if (p_enabled) {
@ -1085,8 +1083,7 @@ void DebugAdapterProtocol::on_debug_stack_dump(const Array &p_stack_dump) {
if (_processing_breakpoint && !p_stack_dump.is_empty()) {
// Find existing breakpoint
Dictionary d = p_stack_dump[0];
DAP::Breakpoint breakpoint;
breakpoint.source.path = ProjectSettings::get_singleton()->globalize_path(d["file"]);
DAP::Breakpoint breakpoint(fetch_source(d["file"]));
breakpoint.line = d["line"];
List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint);
@ -1099,25 +1096,26 @@ void DebugAdapterProtocol::on_debug_stack_dump(const Array &p_stack_dump) {
stackframe_id = 0;
stackframe_list.clear();
scope_list.clear();
// Fill in stacktrace information
for (int i = 0; i < p_stack_dump.size(); i++) {
Dictionary stack_info = p_stack_dump[i];
DAP::StackFrame stackframe;
DAP::StackFrame stackframe(fetch_source(stack_info["file"]));
stackframe.id = stackframe_id++;
stackframe.name = stack_info["function"];
stackframe.line = stack_info["line"];
stackframe.column = 0;
stackframe.source.path = ProjectSettings::get_singleton()->globalize_path(stack_info["file"]);
stackframe.source.compute_checksums();
// Information for "Locals", "Members" and "Globals" variables respectively
List<int> scope_ids;
Vector<int> scope_ids;
for (int j = 0; j < 3; j++) {
scope_ids.push_back(variable_id++);
}
stackframe_list.insert(stackframe, scope_ids);
stackframe_list.push_back(stackframe);
scope_list.insert(stackframe.id, scope_ids);
}
_current_frame = 0;
@ -1126,12 +1124,9 @@ void DebugAdapterProtocol::on_debug_stack_dump(const Array &p_stack_dump) {
void DebugAdapterProtocol::on_debug_stack_frame_vars(const int &p_size) {
_remaining_vars = p_size;
DAP::StackFrame frame;
frame.id = _current_frame;
ERR_FAIL_COND(!stackframe_list.has(frame));
List<int> scope_ids = stackframe_list.find(frame)->value;
for (List<int>::Element *E = scope_ids.front(); E; E = E->next()) {
int var_id = E->get();
ERR_FAIL_COND(!scope_list.has(_current_frame));
Vector<int> scope_ids = scope_list.find(_current_frame)->value;
for (const int &var_id : scope_ids) {
if (variable_list.has(var_id)) {
variable_list.find(var_id)->value.clear();
} else {
@ -1144,11 +1139,9 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) {
DebuggerMarshalls::ScriptStackVariable stack_var;
stack_var.deserialize(p_data);
ERR_FAIL_COND(stackframe_list.is_empty());
DAP::StackFrame frame;
frame.id = _current_frame;
ERR_FAIL_COND(!scope_list.has(_current_frame));
Vector<int> scope_ids = scope_list.find(_current_frame)->value;
List<int> scope_ids = stackframe_list.find(frame)->value;
ERR_FAIL_COND(scope_ids.size() != 3);
ERR_FAIL_INDEX(stack_var.type, 4);
int var_id = scope_ids.get(stack_var.type);
@ -1192,8 +1185,7 @@ void DebugAdapterProtocol::poll() {
on_client_connected();
}
List<Ref<DAPeer>> to_delete;
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
Ref<DAPeer> peer = E->get();
for (const Ref<DAPeer> &peer : clients) {
peer->connection->poll();
StreamPeerTCP::Status status = peer->connection->get_status();
if (status == StreamPeerTCP::STATUS_NONE || status == StreamPeerTCP::STATUS_ERROR) {
@ -1211,8 +1203,8 @@ void DebugAdapterProtocol::poll() {
}
}
for (List<Ref<DAPeer>>::Element *E = to_delete.front(); E; E = E->next()) {
on_client_disconnected(E->get());
for (const Ref<DAPeer> &peer : to_delete) {
on_client_disconnected(peer);
}
to_delete.clear();
}
@ -1225,8 +1217,8 @@ Error DebugAdapterProtocol::start(int p_port, const IPAddress &p_bind_ip) {
}
void DebugAdapterProtocol::stop() {
for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) {
E->get()->connection->disconnect_from_host();
for (const Ref<DAPeer> &peer : clients) {
peer->connection->disconnect_from_host();
}
clients.clear();

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DEBUG_ADAPTER_PROTOCOL_H
#define DEBUG_ADAPTER_PROTOCOL_H
#pragma once
#include "core/debugger/debugger_marshalls.h"
#include "core/io/stream_peer_tcp.h"
@ -68,7 +67,7 @@ struct DAPeer : RefCounted {
Error handle_data();
Error send_data();
String format_output(const Dictionary &p_params) const;
Vector<uint8_t> format_output(const Dictionary &p_params) const;
};
class DebugAdapterProtocol : public Object {
@ -77,6 +76,7 @@ class DebugAdapterProtocol : public Object {
friend class DebugAdapterParser;
using DAPVarID = int;
using DAPStackFrameID = int;
private:
static DebugAdapterProtocol *singleton;
@ -110,6 +110,9 @@ private:
bool request_remote_object(const ObjectID &p_object_id);
bool request_remote_evaluate(const String &p_eval, int p_stack_frame);
const DAP::Source &fetch_source(const String &p_path);
void update_source(const String &p_path);
bool _initialized = false;
bool _processing_breakpoint = false;
bool _stepping = false;
@ -126,7 +129,9 @@ private:
int stackframe_id = 0;
DAPVarID variable_id = 0;
List<DAP::Breakpoint> breakpoint_list;
HashMap<DAP::StackFrame, List<int>, DAP::StackFrame> stackframe_list;
HashMap<String, DAP::Source> breakpoint_source_list;
List<DAP::StackFrame> stackframe_list;
HashMap<DAPStackFrameID, Vector<int>> scope_list;
HashMap<DAPVarID, Array> variable_list;
HashMap<ObjectID, DAPVarID> object_list;
@ -168,5 +173,3 @@ public:
DebugAdapterProtocol();
~DebugAdapterProtocol();
};
#endif // DEBUG_ADAPTER_PROTOCOL_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DEBUG_ADAPTER_SERVER_H
#define DEBUG_ADAPTER_SERVER_H
#pragma once
#include "debug_adapter_protocol.h"
#include "editor/plugins/editor_plugin.h"
@ -54,5 +53,3 @@ public:
void start();
void stop();
};
#endif // DEBUG_ADAPTER_SERVER_H

View file

@ -28,11 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DEBUG_ADAPTER_TYPES_H
#define DEBUG_ADAPTER_TYPES_H
#pragma once
#include "core/io/json.h"
#include "core/variant/dictionary.h"
#include "core/io/file_access.h"
namespace DAP {
@ -69,6 +67,8 @@ public:
void compute_checksums() {
ERR_FAIL_COND(path.is_empty());
_checksums.clear();
// MD5
Checksum md5;
md5.algorithm = "MD5";
@ -102,18 +102,24 @@ public:
struct Breakpoint {
int id = 0;
bool verified = false;
Source source;
const Source *source = nullptr;
int line = 0;
Breakpoint() = default; // Empty constructor is invalid, but is necessary because Godot's collections don't support rvalues.
Breakpoint(const Source &p_source) :
source(&p_source) {}
bool operator==(const Breakpoint &p_other) const {
return source.path == p_other.source.path && line == p_other.line;
return source == p_other.source && line == p_other.line;
}
_FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["id"] = id;
dict["verified"] = verified;
dict["source"] = source.to_json();
if (source) {
dict["source"] = source->to_json();
}
dict["line"] = line;
return dict;
@ -159,9 +165,7 @@ struct Capabilities {
dict["supportsTerminateRequest"] = supportsTerminateRequest;
dict["supportsBreakpointLocationsRequest"] = supportsBreakpointLocationsRequest;
Array arr;
arr.push_back(supportedChecksumAlgorithms[0]);
arr.push_back(supportedChecksumAlgorithms[1]);
Array arr = { supportedChecksumAlgorithms[0], supportedChecksumAlgorithms[1] };
dict["supportedChecksumAlgorithms"] = arr;
return dict;
@ -215,30 +219,25 @@ struct SourceBreakpoint {
struct StackFrame {
int id = 0;
String name;
Source source;
const Source *source = nullptr;
int line = 0;
int column = 0;
StackFrame() = default; // Empty constructor is invalid, but is necessary because Godot's collections don't support rvalues.
StackFrame(const Source &p_source) :
source(&p_source) {}
static uint32_t hash(const StackFrame &p_frame) {
return hash_murmur3_one_32(p_frame.id);
}
bool operator==(const StackFrame &p_other) const {
return id == p_other.id;
}
_FORCE_INLINE_ void from_json(const Dictionary &p_params) {
id = p_params["id"];
name = p_params["name"];
source.from_json(p_params["source"]);
line = p_params["line"];
column = p_params["column"];
}
_FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
dict["id"] = id;
dict["name"] = name;
dict["source"] = source.to_json();
if (source) {
dict["source"] = source->to_json();
}
dict["line"] = line;
dict["column"] = column;
@ -277,5 +276,3 @@ struct Variable {
};
} // namespace DAP
#endif // DEBUG_ADAPTER_TYPES_H

View file

@ -33,29 +33,57 @@
#include "core/debugger/debugger_marshalls.h"
#include "core/io/marshalls.h"
#include "editor/editor_node.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/inspector_dock.h"
#include "scene/debugger/scene_debugger.h"
bool EditorDebuggerRemoteObject::_set(const StringName &p_name, const Variant &p_value) {
if (!prop_values.has(p_name) || String(p_name).begins_with("Constants/")) {
bool EditorDebuggerRemoteObjects::_set(const StringName &p_name, const Variant &p_value) {
return _set_impl(p_name, p_value, "");
}
bool EditorDebuggerRemoteObjects::_set_impl(const StringName &p_name, const Variant &p_value, const String &p_field) {
String name = p_name;
if (name.begins_with("Metadata/")) {
name = name.replace_first("Metadata/", "metadata/");
}
if (!prop_values.has(name) || String(name).begins_with("Constants/")) {
return false;
}
prop_values[p_name] = p_value;
emit_signal(SNAME("value_edited"), remote_object_id, p_name, p_value);
Dictionary &values = prop_values[p_name];
Dictionary old_values = values.duplicate();
for (const uint64_t key : values.keys()) {
values.set(key, p_value);
}
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
const int size = remote_object_ids.size();
ur->create_action(size == 1 ? vformat(TTR("Set %s"), name) : vformat(TTR("Set %s on %d objects"), name, size), UndoRedo::MERGE_ENDS);
ur->add_do_method(this, SNAME("emit_signal"), SNAME("values_edited"), name, values, p_field);
ur->add_undo_method(this, SNAME("emit_signal"), SNAME("values_edited"), name, old_values, p_field);
ur->commit_action();
return true;
}
bool EditorDebuggerRemoteObject::_get(const StringName &p_name, Variant &r_ret) const {
if (!prop_values.has(p_name)) {
bool EditorDebuggerRemoteObjects::_get(const StringName &p_name, Variant &r_ret) const {
String name = p_name;
if (name.begins_with("Metadata/")) {
name = name.replace_first("Metadata/", "metadata/");
}
if (!prop_values.has(name)) {
return false;
}
r_ret = prop_values[p_name];
r_ret = prop_values[p_name][remote_object_ids[0]];
return true;
}
void EditorDebuggerRemoteObject::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->clear(); // Sorry, no want category.
void EditorDebuggerRemoteObjects::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->clear(); // Sorry, don't want any categories.
for (const PropertyInfo &prop : prop_list) {
if (prop.name == "script") {
// Skip the script property, it's always added by the non-virtual method.
@ -66,31 +94,35 @@ void EditorDebuggerRemoteObject::_get_property_list(List<PropertyInfo> *p_list)
}
}
String EditorDebuggerRemoteObject::get_title() {
if (remote_object_id.is_valid()) {
return vformat(TTR("Remote %s:"), String(type_name)) + " " + itos(remote_object_id);
} else {
return "<null>";
}
void EditorDebuggerRemoteObjects::set_property_field(const StringName &p_property, const Variant &p_value, const String &p_field) {
_set_impl(p_property, p_value, p_field);
}
Variant EditorDebuggerRemoteObject::get_variant(const StringName &p_name) {
String EditorDebuggerRemoteObjects::get_title() {
if (!remote_object_ids.is_empty() && ObjectID(remote_object_ids[0].operator uint64_t()).is_valid()) {
const int size = remote_object_ids.size();
return size == 1 ? vformat(TTR("Remote %s: %d"), type_name, remote_object_ids[0]) : vformat(TTR("Remote %s (%d Selected)"), type_name, size);
}
return "<null>";
}
Variant EditorDebuggerRemoteObjects::get_variant(const StringName &p_name) {
Variant var;
_get(p_name, var);
return var;
}
void EditorDebuggerRemoteObject::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_title"), &EditorDebuggerRemoteObject::get_title);
ClassDB::bind_method(D_METHOD("get_variant"), &EditorDebuggerRemoteObject::get_variant);
ClassDB::bind_method(D_METHOD("clear"), &EditorDebuggerRemoteObject::clear);
ClassDB::bind_method(D_METHOD("get_remote_object_id"), &EditorDebuggerRemoteObject::get_remote_object_id);
void EditorDebuggerRemoteObjects::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_title"), &EditorDebuggerRemoteObjects::get_title);
ADD_SIGNAL(MethodInfo("value_edited", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value")));
ADD_SIGNAL(MethodInfo("values_edited", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::DICTIONARY, "values", PROPERTY_HINT_DICTIONARY_TYPE, "uint64_t:Variant"), PropertyInfo(Variant::STRING, "field")));
}
/// EditorDebuggerInspector
EditorDebuggerInspector::EditorDebuggerInspector() {
variables = memnew(EditorDebuggerRemoteObject);
variables = memnew(EditorDebuggerRemoteObjects);
}
EditorDebuggerInspector::~EditorDebuggerInspector() {
@ -100,7 +132,7 @@ EditorDebuggerInspector::~EditorDebuggerInspector() {
void EditorDebuggerInspector::_bind_methods() {
ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("object_edited", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value")));
ADD_SIGNAL(MethodInfo("objects_edited", PropertyInfo(Variant::ARRAY, "ids"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value"), PropertyInfo(Variant::STRING, "field")));
ADD_SIGNAL(MethodInfo("object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
}
@ -111,50 +143,143 @@ void EditorDebuggerInspector::_notification(int p_what) {
} break;
case NOTIFICATION_ENTER_TREE: {
variables->remote_object_ids.append(0);
edit(variables);
} break;
}
}
void EditorDebuggerInspector::_object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value) {
emit_signal(SNAME("object_edited"), p_id, p_prop, p_value);
void EditorDebuggerInspector::_objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field) {
emit_signal(SNAME("objects_edited"), p_prop, p_values, p_field);
}
void EditorDebuggerInspector::_object_selected(ObjectID p_object) {
emit_signal(SNAME("object_selected"), p_object);
}
ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
EditorDebuggerRemoteObject *debug_obj = nullptr;
EditorDebuggerRemoteObjects *EditorDebuggerInspector::set_objects(const Array &p_arr) {
ERR_FAIL_COND_V(p_arr.is_empty(), nullptr);
SceneDebuggerObject obj;
obj.deserialize(p_arr);
ERR_FAIL_COND_V(obj.id.is_null(), ObjectID());
TypedArray<uint64_t> ids;
LocalVector<SceneDebuggerObject> objects;
for (const Array arr : p_arr) {
SceneDebuggerObject obj;
obj.deserialize(arr);
if (obj.id.is_valid()) {
ids.push_back((uint64_t)obj.id);
objects.push_back(obj);
}
}
ERR_FAIL_COND_V(ids.is_empty(), nullptr);
if (remote_objects.has(obj.id)) {
debug_obj = remote_objects[obj.id];
} else {
debug_obj = memnew(EditorDebuggerRemoteObject);
debug_obj->remote_object_id = obj.id;
debug_obj->type_name = obj.class_name;
remote_objects[obj.id] = debug_obj;
debug_obj->connect("value_edited", callable_mp(this, &EditorDebuggerInspector::_object_edited));
// Sorting is necessary, as selected nodes in the remote tree are ordered by index.
ids.sort();
EditorDebuggerRemoteObjects *remote_objects = nullptr;
for (EditorDebuggerRemoteObjects *robjs : remote_objects_list) {
if (robjs->remote_object_ids == ids) {
remote_objects = robjs;
break;
}
}
int old_prop_size = debug_obj->prop_list.size();
if (!remote_objects) {
remote_objects = memnew(EditorDebuggerRemoteObjects);
remote_objects->remote_object_ids = ids;
remote_objects->remote_object_ids.make_read_only();
remote_objects->connect("values_edited", callable_mp(this, &EditorDebuggerInspector::_objects_edited));
remote_objects_list.push_back(remote_objects);
}
debug_obj->prop_list.clear();
StringName class_name = objects[0].class_name;
if (class_name != SNAME("Object")) {
// Search for the common class between all selected objects.
bool check_type_again = true;
while (check_type_again) {
check_type_again = false;
if (class_name == SNAME("Object") || class_name == StringName()) {
// All objects inherit from Object, so no need to continue checking.
class_name = SNAME("Object");
break;
}
// Check that all objects inherit from type_name.
for (const SceneDebuggerObject &obj : objects) {
if (obj.class_name == class_name || ClassDB::is_parent_class(obj.class_name, class_name)) {
continue; // class_name is the same or a parent of the object's class.
}
// class_name is not a parent of the node's class, so check again with the parent class.
class_name = ClassDB::get_parent_class(class_name);
check_type_again = true;
break;
}
}
}
remote_objects->type_name = class_name;
// Search for properties that are present in all selected objects.
struct UsageData {
int qty = 0;
SceneDebuggerObject::SceneDebuggerProperty prop;
TypedDictionary<uint64_t, Variant> values;
};
HashMap<String, UsageData> usage;
int nc = 0;
for (const SceneDebuggerObject &obj : objects) {
for (const SceneDebuggerObject::SceneDebuggerProperty &prop : obj.properties) {
PropertyInfo pinfo = prop.first;
if (pinfo.name == "script") {
continue; // Added later manually, since this is intercepted before being set (check Variant Object::get()).
} else if (pinfo.name.begins_with("metadata/")) {
pinfo.name = pinfo.name.replace_first("metadata/", "Metadata/"); // Trick to not get actual metadata edited from EditorDebuggerRemoteObjects.
}
if (!usage.has(pinfo.name)) {
UsageData usage_dt;
usage_dt.prop = prop;
usage_dt.prop.first.name = pinfo.name;
usage_dt.values[obj.id] = prop.second;
usage[pinfo.name] = usage_dt;
}
// Make sure only properties with the same exact PropertyInfo data will appear.
if (usage[pinfo.name].prop.first == pinfo) {
usage[pinfo.name].qty++;
usage[pinfo.name].values[obj.id] = prop.second;
}
}
nc++;
}
for (HashMap<String, UsageData>::Iterator E = usage.begin(); E;) {
HashMap<String, UsageData>::Iterator next = E;
++next;
UsageData usage_dt = E->value;
if (nc != usage_dt.qty) {
// Doesn't appear on all of them, remove it.
usage.erase(E->key);
}
E = next;
}
int old_prop_size = remote_objects->prop_list.size();
remote_objects->prop_list.clear();
int new_props_added = 0;
HashSet<String> changed;
for (SceneDebuggerObject::SceneDebuggerProperty &property : obj.properties) {
PropertyInfo &pinfo = property.first;
Variant &var = property.second;
for (KeyValue<String, UsageData> &KV : usage) {
const PropertyInfo &pinfo = KV.value.prop.first;
Variant var = KV.value.values[remote_objects->remote_object_ids[0]];
if (pinfo.type == Variant::OBJECT) {
if (var.is_string()) {
String path = var;
if (path.contains("::")) {
// built-in resource
// Built-in resource.
String base_path = path.get_slice("::", 0);
Ref<Resource> dependency = ResourceLoader::load(base_path);
if (dependency.is_valid()) {
@ -162,15 +287,16 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
}
}
var = ResourceLoader::load(path);
KV.value.values[remote_objects->remote_object_ids[0]] = var;
if (pinfo.hint_string == "Script") {
if (debug_obj->get_script() != var) {
debug_obj->set_script(Ref<RefCounted>());
if (remote_objects->get_script() != var) {
remote_objects->set_script(Ref<RefCounted>());
Ref<Script> scr(var);
if (scr.is_valid()) {
ScriptInstance *scr_instance = scr->placeholder_instance_create(debug_obj);
ScriptInstance *scr_instance = scr->placeholder_instance_create(remote_objects);
if (scr_instance) {
debug_obj->set_script_and_instance(var, scr_instance);
remote_objects->set_script_and_instance(var, scr_instance);
}
}
}
@ -178,49 +304,67 @@ ObjectID EditorDebuggerInspector::add_object(const Array &p_arr) {
}
}
//always add the property, since props may have been added or removed
debug_obj->prop_list.push_back(pinfo);
// Always add the property, since props may have been added or removed.
remote_objects->prop_list.push_back(pinfo);
if (!debug_obj->prop_values.has(pinfo.name)) {
if (!remote_objects->prop_values.has(pinfo.name)) {
new_props_added++;
debug_obj->prop_values[pinfo.name] = var;
} else {
if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debug_obj->prop_values[pinfo.name], var))) {
debug_obj->prop_values[pinfo.name] = var;
changed.insert(pinfo.name);
}
} else if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, remote_objects->prop_values[pinfo.name], var))) {
changed.insert(pinfo.name);
}
remote_objects->prop_values[pinfo.name] = KV.value.values;
}
if (old_prop_size == debug_obj->prop_list.size() && new_props_added == 0) {
//only some may have changed, if so, then update those, if exist
if (old_prop_size == remote_objects->prop_list.size() && new_props_added == 0) {
// Only some may have changed, if so, then update those, if they exist.
for (const String &E : changed) {
emit_signal(SNAME("object_property_updated"), debug_obj->remote_object_id, E);
emit_signal(SNAME("object_property_updated"), remote_objects->get_instance_id(), E);
}
} else {
//full update, because props were added or removed
debug_obj->update();
// Full update, because props were added or removed.
remote_objects->update();
}
return remote_objects;
}
void EditorDebuggerInspector::clear_remote_inspector() {
if (remote_objects_list.is_empty()) {
return;
}
const Object *obj = InspectorDock::get_inspector_singleton()->get_edited_object();
// Check if the inspector holds remote items, and take it out if so.
if (Object::cast_to<EditorDebuggerRemoteObjects>(obj)) {
EditorNode::get_singleton()->push_item(nullptr);
}
return obj.id;
}
void EditorDebuggerInspector::clear_cache() {
for (const KeyValue<ObjectID, EditorDebuggerRemoteObject *> &E : remote_objects) {
EditorNode *editor = EditorNode::get_singleton();
if (editor->get_editor_selection_history()->get_current() == E.value->get_instance_id()) {
editor->push_item(nullptr);
}
memdelete(E.value);
clear_remote_inspector();
for (EditorDebuggerRemoteObjects *robjs : remote_objects_list) {
memdelete(robjs);
}
remote_objects.clear();
remote_objects_list.clear();
remote_dependencies.clear();
}
Object *EditorDebuggerInspector::get_object(ObjectID p_id) {
if (remote_objects.has(p_id)) {
return remote_objects[p_id];
void EditorDebuggerInspector::invalidate_selection_from_cache(const TypedArray<uint64_t> &p_ids) {
for (EditorDebuggerRemoteObjects *robjs : remote_objects_list) {
if (robjs->remote_object_ids == p_ids) {
const Object *obj = InspectorDock::get_inspector_singleton()->get_edited_object();
if (obj == robjs) {
EditorNode::get_singleton()->push_item(nullptr);
}
remote_objects_list.erase(robjs);
memdelete(robjs);
break;
}
}
return nullptr;
}
void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_offset) {
@ -270,7 +414,7 @@ void EditorDebuggerInspector::add_stack_variable(const Array &p_array, int p_off
}
variables->prop_list.insert_before(current, pinfo);
}
variables->prop_values[type + n] = v;
variables->prop_values[type + n][0] = v;
variables->update();
edit(variables);
@ -288,9 +432,9 @@ void EditorDebuggerInspector::clear_stack_variables() {
}
String EditorDebuggerInspector::get_stack_variable(const String &p_var) {
for (KeyValue<StringName, Variant> &E : variables->prop_values) {
for (KeyValue<StringName, TypedDictionary<uint64_t, Variant>> &E : variables->prop_values) {
String v = E.key.operator String();
if (v.get_slice("/", 1) == p_var) {
if (v.get_slicec('/', 1) == p_var) {
return variables->get_variant(v);
}
}

View file

@ -28,13 +28,18 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DEBUGGER_INSPECTOR_H
#define EDITOR_DEBUGGER_INSPECTOR_H
#pragma once
#include "core/variant/typed_dictionary.h"
#include "editor/editor_inspector.h"
class EditorDebuggerRemoteObject : public Object {
GDCLASS(EditorDebuggerRemoteObject, Object);
class SceneDebuggerObject;
class EditorDebuggerRemoteObjects : public Object {
GDCLASS(EditorDebuggerRemoteObjects, Object);
private:
bool _set_impl(const StringName &p_name, const Variant &p_value, const String &p_field);
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@ -43,14 +48,13 @@ protected:
static void _bind_methods();
public:
ObjectID remote_object_id;
TypedArray<uint64_t> remote_object_ids;
String type_name;
List<PropertyInfo> prop_list;
HashMap<StringName, Variant> prop_values;
HashMap<StringName, TypedDictionary<uint64_t, Variant>> prop_values;
ObjectID get_remote_object_id() { return remote_object_id; }
void set_property_field(const StringName &p_property, const Variant &p_value, const String &p_field);
String get_title();
Variant get_variant(const StringName &p_name);
void clear() {
@ -59,21 +63,18 @@ public:
}
void update() { notify_property_list_changed(); }
EditorDebuggerRemoteObject() {}
};
class EditorDebuggerInspector : public EditorInspector {
GDCLASS(EditorDebuggerInspector, EditorInspector);
private:
ObjectID inspected_object_id;
HashMap<ObjectID, EditorDebuggerRemoteObject *> remote_objects;
LocalVector<EditorDebuggerRemoteObjects *> remote_objects_list;
HashSet<Ref<Resource>> remote_dependencies;
EditorDebuggerRemoteObject *variables = nullptr;
EditorDebuggerRemoteObjects *variables = nullptr;
void _object_selected(ObjectID p_object);
void _object_edited(ObjectID p_id, const String &p_prop, const Variant &p_value);
void _objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field);
protected:
void _notification(int p_what);
@ -84,14 +85,13 @@ public:
~EditorDebuggerInspector();
// Remote Object cache
ObjectID add_object(const Array &p_arr);
Object *get_object(ObjectID p_id);
EditorDebuggerRemoteObjects *set_objects(const Array &p_array);
void clear_remote_inspector();
void clear_cache();
void invalidate_selection_from_cache(const TypedArray<uint64_t> &p_ids);
// Stack Dump variables
String get_stack_variable(const String &p_var);
void add_stack_variable(const Array &p_arr, int p_offset = -1);
void clear_stack_variables();
};
#endif // EDITOR_DEBUGGER_INSPECTOR_H

View file

@ -82,7 +82,8 @@ EditorDebuggerNode::EditorDebuggerNode() {
// Remote scene tree
remote_scene_tree = memnew(EditorDebuggerTree);
remote_scene_tree->connect("object_selected", callable_mp(this, &EditorDebuggerNode::_remote_object_requested));
remote_scene_tree->connect("objects_selected", callable_mp(this, &EditorDebuggerNode::_remote_objects_requested));
remote_scene_tree->connect("selection_cleared", callable_mp(this, &EditorDebuggerNode::_remote_selection_cleared));
remote_scene_tree->connect("save_node", callable_mp(this, &EditorDebuggerNode::_save_node_requested));
remote_scene_tree->connect("button_clicked", callable_mp(this, &EditorDebuggerNode::_remote_tree_button_pressed));
SceneTreeDock::get_singleton()->add_remote_tree_editor(remote_scene_tree);
@ -109,11 +110,13 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id));
node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution));
node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked).bind(id));
node->connect("debug_data", callable_mp(this, &EditorDebuggerNode::_debug_data).bind(id));
node->connect("remote_tree_select_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_select_requested).bind(id));
node->connect("remote_tree_clear_selection_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_clear_selection_requested).bind(id));
node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated).bind(id));
node->connect("remote_object_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_updated).bind(id));
node->connect("remote_objects_updated", callable_mp(this, &EditorDebuggerNode::_remote_objects_updated).bind(id));
node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id));
node->connect("remote_object_requested", callable_mp(this, &EditorDebuggerNode::_remote_object_requested).bind(id));
node->connect("remote_objects_requested", callable_mp(this, &EditorDebuggerNode::_remote_objects_requested).bind(id));
node->connect("set_breakpoint", callable_mp(this, &EditorDebuggerNode::_breakpoint_set_in_tree).bind(id));
node->connect("clear_breakpoints", callable_mp(this, &EditorDebuggerNode::_breakpoints_cleared_in_tree).bind(id));
node->connect("errors_cleared", callable_mp(this, &EditorDebuggerNode::_update_errors));
@ -222,10 +225,6 @@ void EditorDebuggerNode::register_undo_redo(UndoRedo *p_undo_redo) {
p_undo_redo->set_property_notify_callback(_properties_changed, this);
}
EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() {
return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_current()));
}
ScriptEditorDebugger *EditorDebuggerNode::get_debugger(int p_id) const {
return Object::cast_to<ScriptEditorDebugger>(tabs->get_tab_control(p_id));
}
@ -292,6 +291,10 @@ void EditorDebuggerNode::stop(bool p_force) {
if (keep_open && !p_force) {
return;
}
remote_scene_tree_wait = false;
inspect_edited_object_wait = false;
current_uri.clear();
if (server.is_valid()) {
server->stop();
@ -304,6 +307,7 @@ void EditorDebuggerNode::stop(bool p_force) {
server.unref();
}
// Also close all debugging sessions.
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
if (dbg->is_session_active()) {
@ -351,21 +355,29 @@ void EditorDebuggerNode::_notification(int p_what) {
_update_errors();
// Remote scene tree update
remote_scene_tree_timeout -= get_process_delta_time();
if (remote_scene_tree_timeout < 0) {
remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");
if (remote_scene_tree->is_visible_in_tree()) {
get_current_debugger()->request_remote_tree();
// Remote scene tree update.
if (!remote_scene_tree_wait) {
remote_scene_tree_timeout -= get_process_delta_time();
if (remote_scene_tree_timeout < 0) {
remote_scene_tree_timeout = EDITOR_GET("debugger/remote_scene_tree_refresh_interval");
if (remote_scene_tree->is_visible_in_tree()) {
remote_scene_tree_wait = true;
get_current_debugger()->request_remote_tree();
}
}
}
// Remote inspector update
inspect_edited_object_timeout -= get_process_delta_time();
if (inspect_edited_object_timeout < 0) {
inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
get_current_debugger()->request_remote_object(obj->remote_object_id);
// Remote inspector update.
if (!inspect_edited_object_wait) {
inspect_edited_object_timeout -= get_process_delta_time();
if (inspect_edited_object_timeout < 0) {
inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");
if (EditorDebuggerRemoteObjects *robjs = Object::cast_to<EditorDebuggerRemoteObjects>(InspectorDock::get_inspector_singleton()->get_edited_object())) {
inspect_edited_object_wait = true;
get_current_debugger()->request_remote_objects(robjs->remote_object_ids, false);
}
}
}
@ -467,21 +479,26 @@ void EditorDebuggerNode::_debugger_stopped(int p_id) {
void EditorDebuggerNode::_debugger_wants_stop(int p_id) {
// Ask editor to kill PID.
int pid = get_debugger(p_id)->get_remote_pid();
if (pid) {
if (int pid = get_debugger(p_id)->get_remote_pid()) {
callable_mp(EditorNode::get_singleton(), &EditorNode::stop_child_process).call_deferred(pid);
}
}
void EditorDebuggerNode::_debugger_changed(int p_tab) {
if (get_inspected_remote_object()) {
// Clear inspected object, you can only inspect objects in selected debugger.
// Hopefully, in the future, we will have one inspector per debugger.
EditorNode::get_singleton()->push_item(nullptr);
remote_scene_tree_wait = false;
inspect_edited_object_wait = false;
if (Object *robjs = InspectorDock::get_inspector_singleton()->get_edited_object()) {
if (Object::cast_to<EditorDebuggerRemoteObjects>(robjs)) {
// Clear inspected object, you can only inspect objects in selected debugger.
// Hopefully, in the future, we will have one inspector per debugger.
EditorNode::get_singleton()->push_item(nullptr);
}
}
if (get_previous_debugger()) {
_text_editor_stack_clear(get_previous_debugger());
if (ScriptEditorDebugger *prev_debug = get_previous_debugger()) {
prev_debug->clear_inspector();
_text_editor_stack_clear(prev_debug);
}
if (remote_scene_tree->is_visible_in_tree()) {
get_current_debugger()->request_remote_tree();
@ -493,6 +510,18 @@ void EditorDebuggerNode::_debugger_changed(int p_tab) {
_break_state_changed();
}
void EditorDebuggerNode::_debug_data(const String &p_msg, const Array &p_data, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
if (p_msg == "scene:scene_tree") {
remote_scene_tree_wait = false;
} else if (p_msg == "scene:inspect_objects") {
inspect_edited_object_wait = false;
}
}
void EditorDebuggerNode::set_script_debug_button(MenuButton *p_button) {
script_menu = p_button;
script_menu->set_text(TTR("Debug"));
@ -587,6 +616,10 @@ bool EditorDebuggerNode::is_skip_breakpoints() const {
return get_current_debugger()->is_skip_breakpoints();
}
bool EditorDebuggerNode::is_ignore_error_breaks() const {
return get_default_debugger()->is_ignore_error_breaks();
}
void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p_enabled) {
breakpoints[Breakpoint(p_path, p_line)] = p_enabled;
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
@ -646,11 +679,39 @@ void EditorDebuggerNode::request_remote_tree() {
get_current_debugger()->request_remote_tree();
}
void EditorDebuggerNode::_remote_tree_select_requested(ObjectID p_id, int p_debugger) {
void EditorDebuggerNode::set_remote_selection(const TypedArray<int64_t> &p_ids) {
stop_waiting_inspection();
get_current_debugger()->request_remote_objects(p_ids);
}
void EditorDebuggerNode::clear_remote_tree_selection() {
remote_scene_tree->clear_selection();
get_current_debugger()->clear_inspector(remote_scene_tree_clear_msg);
}
void EditorDebuggerNode::stop_waiting_inspection() {
inspect_edited_object_timeout = EDITOR_GET("debugger/remote_inspect_refresh_interval");
inspect_edited_object_wait = false;
}
bool EditorDebuggerNode::match_remote_selection(const TypedArray<uint64_t> &p_ids) const {
return p_ids == remote_scene_tree->get_selection();
}
void EditorDebuggerNode::_remote_tree_select_requested(const TypedArray<int64_t> &p_ids, int p_debugger) {
if (p_debugger == tabs->get_current_tab()) {
remote_scene_tree->select_nodes(p_ids);
}
}
void EditorDebuggerNode::_remote_tree_clear_selection_requested(int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
remote_scene_tree->select_node(p_id);
remote_scene_tree->clear_selection();
remote_scene_tree_clear_msg = false;
get_current_debugger()->clear_inspector(false);
remote_scene_tree_clear_msg = true;
}
void EditorDebuggerNode::_remote_tree_updated(int p_debugger) {
@ -679,37 +740,37 @@ void EditorDebuggerNode::_remote_tree_button_pressed(Object *p_item, int p_colum
}
}
void EditorDebuggerNode::_remote_object_updated(ObjectID p_id, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
void EditorDebuggerNode::_remote_objects_updated(EditorDebuggerRemoteObjects *p_objs, int p_debugger) {
if (p_debugger == tabs->get_current_tab() && p_objs != InspectorDock::get_inspector_singleton()->get_edited_object()) {
EditorNode::get_singleton()->push_item(p_objs);
}
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
if (obj->remote_object_id == p_id) {
return; // Already being edited
}
}
EditorNode::get_singleton()->push_item(get_current_debugger()->get_remote_object(p_id));
}
void EditorDebuggerNode::_remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
if (EditorDebuggerRemoteObject *obj = get_inspected_remote_object()) {
if (obj->remote_object_id != p_id) {
return;
}
Object *obj = InspectorDock::get_inspector_singleton()->get_edited_object();
if (obj && obj->get_instance_id() == p_id) {
InspectorDock::get_inspector_singleton()->update_property(p_property);
}
}
void EditorDebuggerNode::_remote_object_requested(ObjectID p_id, int p_debugger) {
void EditorDebuggerNode::_remote_objects_requested(const TypedArray<uint64_t> &p_ids, int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
inspect_edited_object_timeout = 0.7; // Temporarily disable timeout to avoid multiple requests.
get_current_debugger()->request_remote_object(p_id);
stop_waiting_inspection();
get_current_debugger()->request_remote_objects(p_ids);
}
void EditorDebuggerNode::_remote_selection_cleared(int p_debugger) {
if (p_debugger != tabs->get_current_tab()) {
return;
}
stop_waiting_inspection();
get_current_debugger()->clear_inspector();
}
void EditorDebuggerNode::_save_node_requested(ObjectID p_id, const String &p_file, int p_debugger) {
@ -809,6 +870,17 @@ void EditorDebuggerNode::live_debug_reparent_node(const NodePath &p_at, const No
});
}
void EditorDebuggerNode::set_debug_mute_audio(bool p_mute) {
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
dbg->set_debug_mute_audio(p_mute);
});
debug_mute_audio = p_mute;
}
bool EditorDebuggerNode::get_debug_mute_audio() const {
return debug_mute_audio;
}
void EditorDebuggerNode::set_camera_override(CameraOverride p_override) {
_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
dbg->set_camera_override(p_override);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DEBUGGER_NODE_H
#define EDITOR_DEBUGGER_NODE_H
#pragma once
#include "core/object/script_language.h"
#include "editor/debugger/editor_debugger_server.h"
@ -39,7 +38,7 @@ class Button;
class DebugAdapterParser;
class EditorDebuggerPlugin;
class EditorDebuggerTree;
class EditorDebuggerRemoteObject;
class EditorDebuggerRemoteObjects;
class MenuButton;
class ScriptEditorDebugger;
class TabContainer;
@ -103,21 +102,25 @@ private:
int last_error_count = 0;
int last_warning_count = 0;
bool inspect_edited_object_wait = false;
float inspect_edited_object_timeout = 0;
EditorDebuggerTree *remote_scene_tree = nullptr;
bool remote_scene_tree_wait = false;
float remote_scene_tree_timeout = 0.0;
bool remote_scene_tree_clear_msg = true;
bool auto_switch_remote_scene_tree = false;
bool debug_with_external_editor = false;
bool keep_open = false;
String current_uri;
bool debug_mute_audio = false;
CameraOverride camera_override = OVERRIDE_NONE;
HashMap<Breakpoint, bool, Breakpoint> breakpoints;
HashSet<Ref<EditorDebuggerPlugin>> debugger_plugins;
ScriptEditorDebugger *_add_debugger();
EditorDebuggerRemoteObject *get_inspected_remote_object();
void _update_errors();
friend class DebuggerEditorPlugin;
@ -129,12 +132,15 @@ protected:
void _debugger_stopped(int p_id);
void _debugger_wants_stop(int p_id);
void _debugger_changed(int p_tab);
void _remote_tree_select_requested(ObjectID p_id, int p_debugger);
void _debug_data(const String &p_msg, const Array &p_data, int p_debugger);
void _remote_tree_select_requested(const TypedArray<int64_t> &p_ids, int p_debugger);
void _remote_tree_clear_selection_requested(int p_debugger);
void _remote_tree_updated(int p_debugger);
void _remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void _remote_object_updated(ObjectID p_id, int p_debugger);
void _remote_objects_updated(EditorDebuggerRemoteObjects *p_objs, int p_debugger);
void _remote_object_property_updated(ObjectID p_id, const String &p_property, int p_debugger);
void _remote_object_requested(ObjectID p_id, int p_debugger);
void _remote_objects_requested(const TypedArray<uint64_t> &p_ids, int p_debugger);
void _remote_selection_cleared(int p_debugger);
void _save_node_requested(ObjectID p_id, const String &p_file, int p_debugger);
void _breakpoint_set_in_tree(Ref<RefCounted> p_script, int p_line, bool p_enabled, int p_debugger);
@ -184,6 +190,7 @@ public:
bool get_debug_with_external_editor() { return debug_with_external_editor; }
bool is_skip_breakpoints() const;
bool is_ignore_error_breaks() const;
void set_breakpoint(const String &p_path, int p_line, bool p_enabled);
void set_breakpoints(const String &p_path, const Array &p_lines);
void reload_all_scripts();
@ -191,6 +198,10 @@ public:
// Remote inspector/edit.
void request_remote_tree();
void set_remote_selection(const TypedArray<int64_t> &p_ids);
void clear_remote_tree_selection();
void stop_waiting_inspection();
bool match_remote_selection(const TypedArray<uint64_t> &p_ids) const;
static void _methods_changed(void *p_ud, Object *p_base, const StringName &p_name, const Variant **p_args, int p_argcount);
static void _properties_changed(void *p_ud, Object *p_base, const StringName &p_property, const Variant &p_value);
@ -205,6 +216,9 @@ public:
void live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name);
void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
void set_debug_mute_audio(bool p_mute);
bool get_debug_mute_audio() const;
void set_camera_override(CameraOverride p_override);
CameraOverride get_camera_override();
@ -218,5 +232,3 @@ public:
void add_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
void remove_debugger_plugin(const Ref<EditorDebuggerPlugin> &p_plugin);
};
#endif // EDITOR_DEBUGGER_NODE_H

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DEBUGGER_SERVER_H
#define EDITOR_DEBUGGER_SERVER_H
#pragma once
#include "core/debugger/remote_debugger_peer.h"
#include "core/object/ref_counted.h"
@ -56,5 +55,3 @@ public:
virtual bool is_connection_available() const = 0;
virtual Ref<RemoteDebuggerPeer> take_connection() = 0;
};
#endif // EDITOR_DEBUGGER_SERVER_H

View file

@ -35,6 +35,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/gui/editor_file_dialog.h"
#include "editor/gui/editor_toaster.h"
#include "editor/scene_tree_dock.h"
#include "scene/debugger/scene_debugger.h"
#include "scene/gui/texture_rect.h"
@ -44,6 +45,7 @@
EditorDebuggerTree::EditorDebuggerTree() {
set_v_size_flags(SIZE_EXPAND_FILL);
set_allow_rmb_select(true);
set_select_mode(SELECT_MULTI);
// Popup
item_menu = memnew(PopupMenu);
@ -54,6 +56,9 @@ EditorDebuggerTree::EditorDebuggerTree() {
file_dialog = memnew(EditorFileDialog);
file_dialog->connect("file_selected", callable_mp(this, &EditorDebuggerTree::_file_selected));
add_child(file_dialog);
accept = memnew(AcceptDialog);
add_child(accept);
}
void EditorDebuggerTree::_notification(int p_what) {
@ -61,7 +66,8 @@ void EditorDebuggerTree::_notification(int p_what) {
case NOTIFICATION_POSTINITIALIZE: {
set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
connect("cell_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_selected));
connect("multi_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_selection_changed));
connect("nothing_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_nothing_selected));
connect("item_collapsed", callable_mp(this, &EditorDebuggerTree::_scene_tree_folded));
connect("item_mouse_selected", callable_mp(this, &EditorDebuggerTree::_scene_tree_rmb_selected));
} break;
@ -73,24 +79,57 @@ void EditorDebuggerTree::_notification(int p_what) {
}
void EditorDebuggerTree::_bind_methods() {
ADD_SIGNAL(MethodInfo("object_selected", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("objects_selected", PropertyInfo(Variant::ARRAY, "object_ids"), PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("selection_cleared", PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("save_node", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "filename"), PropertyInfo(Variant::INT, "debugger")));
ADD_SIGNAL(MethodInfo("open"));
}
void EditorDebuggerTree::_scene_tree_selected() {
if (updating_scene_tree) {
void EditorDebuggerTree::_scene_tree_selection_changed(TreeItem *p_item, int p_column, bool p_selected) {
if (updating_scene_tree || !p_item) {
return;
}
TreeItem *item = get_selected();
if (!item) {
return;
uint64_t id = uint64_t(p_item->get_metadata(0));
if (p_selected) {
if (inspected_object_ids.size() == (int)EDITOR_GET("debugger/max_node_selection")) {
selection_surpassed_limit = true;
p_item->deselect(0);
return;
}
if (!inspected_object_ids.has(id)) {
inspected_object_ids.append(id);
}
} else if (inspected_object_ids.has(id)) {
inspected_object_ids.erase(id);
}
inspected_object_id = uint64_t(item->get_metadata(0));
if (!notify_selection_queued) {
callable_mp(this, &EditorDebuggerTree::_notify_selection_changed).call_deferred();
notify_selection_queued = true;
}
}
emit_signal(SNAME("object_selected"), inspected_object_id, debugger_id);
void EditorDebuggerTree::_scene_tree_nothing_selected() {
deselect_all();
inspected_object_ids.clear();
emit_signal(SNAME("selection_cleared"), debugger_id);
}
void EditorDebuggerTree::_notify_selection_changed() {
notify_selection_queued = false;
if (inspected_object_ids.is_empty()) {
emit_signal(SNAME("selection_cleared"), debugger_id);
} else {
emit_signal(SNAME("objects_selected"), inspected_object_ids.duplicate(), debugger_id);
}
if (selection_surpassed_limit) {
selection_surpassed_limit = false;
EditorToaster::get_singleton()->popup_str(vformat(TTR("Some remote nodes were not selected, as the configured maximum selection is %d. This can be changed at \"debugger/max_node_selection\" in the Editor Settings."), EDITOR_GET("debugger/max_node_selection")), EditorToaster::SEVERITY_WARNING);
}
}
void EditorDebuggerTree::_scene_tree_folded(Object *p_obj) {
@ -124,7 +163,7 @@ void EditorDebuggerTree::_scene_tree_rmb_selected(const Vector2 &p_position, Mou
item->select(0);
item_menu->clear();
item_menu->add_icon_item(get_editor_theme_icon(SNAME("CreateNewSceneFrom")), TTR("Save Branch as Scene"), ITEM_MENU_SAVE_REMOTE_NODE);
item_menu->add_icon_item(get_editor_theme_icon(SNAME("CreateNewSceneFrom")), TTR("Save Branch as Scene..."), ITEM_MENU_SAVE_REMOTE_NODE);
item_menu->add_icon_item(get_editor_theme_icon(SNAME("CopyNodePath")), TTR("Copy Node Path"), ITEM_MENU_COPY_NODE_PATH);
item_menu->add_icon_item(get_editor_theme_icon(SNAME("Collapse")), TTR("Expand/Collapse Branch"), ITEM_MENU_EXPAND_COLLAPSE);
item_menu->set_position(get_screen_position() + get_local_mouse_position());
@ -152,12 +191,13 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
updating_scene_tree = true;
const String last_path = get_selected_path();
const String filter = SceneTreeDock::get_singleton()->get_filter();
TreeItem *select_item = nullptr;
LocalVector<TreeItem *> select_items;
bool hide_filtered_out_parents = EDITOR_GET("docks/scene_tree/hide_filtered_out_parents");
bool should_scroll = scrolling_to_item || filter != last_filter;
scrolling_to_item = false;
TreeItem *scroll_item = nullptr;
TypedArray<uint64_t> ids_present;
// Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion.
List<ParentItem> parents;
@ -216,9 +256,11 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
}
item->set_meta("node_path", current_path + "/" + item->get_text(0));
// Select previously selected node.
// Select previously selected nodes.
if (debugger_id == p_debugger) { // Can use remote id.
if (node.id == inspected_object_id) {
if (inspected_object_ids.has(uint64_t(node.id))) {
ids_present.append(node.id);
if (selection_uncollapse_all) {
selection_uncollapse_all = false;
@ -228,14 +270,14 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
updating_scene_tree = true;
}
select_item = item;
select_items.push_back(item);
if (should_scroll) {
scroll_item = item;
}
}
} else if (last_path == (String)item->get_meta("node_path")) { // Must use path.
updating_scene_tree = false; // Force emission of new selection.
select_item = item;
updating_scene_tree = false; // Force emission of new selections.
select_items.push_back(item);
if (should_scroll) {
scroll_item = item;
}
@ -280,12 +322,12 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
break; // Filter matches, must survive.
}
parent->remove_child(item);
memdelete(item);
if (select_item == item || scroll_item == item) {
select_item = nullptr;
if (select_items.has(item) || scroll_item == item) {
select_items.resize(select_items.size() - 1);
scroll_item = nullptr;
}
parent->remove_child(item);
memdelete(item);
if (had_siblings) {
break; // Parent must survive.
@ -316,18 +358,20 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
from->get_parent()->remove_child(from);
memdelete(from);
if (select_item == from || scroll_item == from) {
select_item = nullptr;
if (select_items.has(from) || scroll_item == from) {
select_items.erase(from);
scroll_item = nullptr;
}
}
}
}
inspected_object_ids = ids_present;
debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree.
if (select_item) {
select_item->select(0);
for (TreeItem *item : select_items) {
item->select(0);
}
if (scroll_item) {
scroll_to_item(scroll_item, false);
@ -337,12 +381,22 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int
updating_scene_tree = false;
}
void EditorDebuggerTree::select_node(ObjectID p_id) {
void EditorDebuggerTree::select_nodes(const TypedArray<int64_t> &p_ids) {
// Manually select, as the tree control may be out-of-date for some reason (e.g. not shown yet).
selection_uncollapse_all = true;
inspected_object_id = uint64_t(p_id);
inspected_object_ids = p_ids;
scrolling_to_item = true;
emit_signal(SNAME("object_selected"), inspected_object_id, debugger_id);
if (!updating_scene_tree) {
// Request a tree refresh.
EditorDebuggerNode::get_singleton()->request_remote_tree();
}
// Set the value immediately, so no update flooding happens and causes a crash.
updating_scene_tree = true;
}
void EditorDebuggerTree::clear_selection() {
inspected_object_ids.clear();
if (!updating_scene_tree) {
// Request a tree refresh.
@ -453,8 +507,11 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
}
void EditorDebuggerTree::_file_selected(const String &p_file) {
if (inspected_object_id.is_null()) {
if (inspected_object_ids.size() != 1) {
accept->set_text(vformat(TTR("Saving the branch as a scene requires selecting only one node, but you have selected %d nodes."), inspected_object_ids.size()));
accept->popup_centered();
return;
}
emit_signal(SNAME("save_node"), inspected_object_id, p_file, debugger_id);
emit_signal(SNAME("save_node"), inspected_object_ids[0], p_file, debugger_id);
}

View file

@ -28,11 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_DEBUGGER_TREE_H
#define EDITOR_DEBUGGER_TREE_H
#pragma once
#include "scene/gui/tree.h"
class AcceptDialog;
class SceneDebuggerTree;
class EditorFileDialog;
@ -58,18 +58,23 @@ private:
ITEM_MENU_EXPAND_COLLAPSE,
};
ObjectID inspected_object_id;
TypedArray<uint64_t> inspected_object_ids;
int debugger_id = 0;
bool updating_scene_tree = false;
bool scrolling_to_item = false;
bool notify_selection_queued = false;
bool selection_surpassed_limit = false;
bool selection_uncollapse_all = false;
HashSet<ObjectID> unfold_cache;
PopupMenu *item_menu = nullptr;
EditorFileDialog *file_dialog = nullptr;
AcceptDialog *accept = nullptr;
String last_filter;
void _scene_tree_folded(Object *p_obj);
void _scene_tree_selected();
void _scene_tree_selection_changed(TreeItem *p_item, int p_column, bool p_selected);
void _scene_tree_nothing_selected();
void _notify_selection_changed();
void _scene_tree_rmb_selected(const Vector2 &p_position, MouseButton p_button);
void _item_menu_id_pressed(int p_option);
void _file_selected(const String &p_file);
@ -90,9 +95,10 @@ public:
String get_selected_path();
ObjectID get_selected_object();
int get_current_debugger(); // Would love to have one tree for every debugger.
inline TypedArray<uint64_t> get_selection() const { return inspected_object_ids.duplicate(); }
void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger);
void select_node(ObjectID p_id);
void select_nodes(const TypedArray<int64_t> &p_ids);
void clear_selection();
EditorDebuggerTree();
};
#endif // EDITOR_DEBUGGER_TREE_H

View file

@ -74,7 +74,8 @@ void EditorExpressionEvaluator::_clear() {
}
void EditorExpressionEvaluator::_remote_object_selected(ObjectID p_id) {
editor_debugger->emit_signal(SNAME("remote_object_requested"), p_id);
Array arr = { p_id };
editor_debugger->emit_signal(SNAME("remote_objects_requested"), arr);
}
void EditorExpressionEvaluator::_on_expression_input_changed(const String &p_expression) {
@ -109,6 +110,7 @@ EditorExpressionEvaluator::EditorExpressionEvaluator() {
expression_input = memnew(LineEdit);
expression_input->set_h_size_flags(Control::SIZE_EXPAND_FILL);
expression_input->set_placeholder(TTR("Expression to evaluate"));
expression_input->set_accessibility_name(TTRC("Expression"));
expression_input->set_clear_button_enabled(true);
expression_input->connect(SceneStringName(text_submitted), callable_mp(this, &EditorExpressionEvaluator::_evaluate).unbind(1));
expression_input->connect(SceneStringName(text_changed), callable_mp(this, &EditorExpressionEvaluator::_on_expression_input_changed));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_EXPRESSION_EVALUATOR_H
#define EDITOR_EXPRESSION_EVALUATOR_H
#pragma once
#include "scene/gui/box_container.h"
@ -73,5 +72,3 @@ public:
EditorExpressionEvaluator();
};
#endif // EDITOR_EXPRESSION_EVALUATOR_H

View file

@ -78,7 +78,7 @@ void EditorFileServer::_scan_files_changed(EditorFileSystemDirectory *efd, const
uint64_t mt = FileAccess::get_modified_time(remapped_path);
_add_file(remapped_path, mt, files_to_send, cached_files);
} else if (remap.begins_with("path.")) {
String feature = remap.get_slice(".", 1);
String feature = remap.get_slicec('.', 1);
if (p_tags.has(feature)) {
String remapped_path = cf->get_value("remap", remap);
uint64_t mt = FileAccess::get_modified_time(remapped_path);
@ -200,7 +200,7 @@ void EditorFileServer::poll() {
// Scan files to send.
_scan_files_changed(EditorFileSystem::get_singleton()->get_filesystem(), tags, files_to_send, cached_files);
// Add forced export files
Vector<String> forced_export = EditorExportPlatform::get_forced_export_files();
Vector<String> forced_export = EditorExportPlatform::get_forced_export_files(Ref<EditorExportPreset>());
for (int i = 0; i < forced_export.size(); i++) {
_add_custom_file(forced_export[i], files_to_send, cached_files);
}

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_FILE_SERVER_H
#define EDITOR_FILE_SERVER_H
#pragma once
#include "core/io/tcp_server.h"
#include "core/os/thread.h"
@ -55,5 +54,3 @@ public:
EditorFileServer();
~EditorFileServer();
};
#endif // EDITOR_FILE_SERVER_H

View file

@ -37,8 +37,6 @@
#include "editor/themes/editor_theme_manager.h"
#include "main/performance.h"
EditorPerformanceProfiler::Monitor::Monitor() {}
EditorPerformanceProfiler::Monitor::Monitor(const String &p_name, const String &p_base, int p_frame_index, Performance::MonitorType p_type, TreeItem *p_item) {
type = p_type;
item = p_item;

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_PERFORMANCE_PROFILER_H
#define EDITOR_PERFORMANCE_PROFILER_H
#pragma once
#include "core/templates/hash_map.h"
#include "main/performance.h"
@ -52,7 +51,7 @@ private:
Performance::MonitorType type = Performance::MONITOR_TYPE_QUANTITY;
int frame_index = 0;
Monitor();
Monitor() {}
Monitor(const String &p_name, const String &p_base, int p_frame_index, Performance::MonitorType p_type, TreeItem *p_item);
void update_value(float p_value);
void reset();
@ -88,5 +87,3 @@ public:
List<float> *get_monitor_data(const StringName &p_name);
EditorPerformanceProfiler();
};
#endif // EDITOR_PERFORMANCE_PROFILER_H

View file

@ -146,7 +146,7 @@ String EditorProfiler::_get_time_as_text(const Metric &m, float p_time, int p_ca
Color EditorProfiler::_get_color_from_signature(const StringName &p_signature) const {
Color bc = get_theme_color(SNAME("error_color"), EditorStringName(Editor));
double rot = ABS(double(p_signature.hash()) / double(0x7FFFFFFF));
double rot = Math::abs(double(p_signature.hash()) / double(0x7FFFFFFF));
Color c;
c.set_hsv(rot, bc.get_s(), bc.get_v());
return c.lerp(get_theme_color(SNAME("base_color"), EditorStringName(Editor)), 0.07);
@ -694,6 +694,7 @@ EditorProfiler::EditorProfiler() {
hb_measure->add_child(memnew(Label(TTR("Measure:"))));
display_mode = memnew(OptionButton);
display_mode->set_accessibility_name(TTRC("Measure"));
display_mode->add_item(TTR("Frame Time (ms)"));
display_mode->add_item(TTR("Average Time (ms)"));
display_mode->add_item(TTR("Frame %"));
@ -709,6 +710,7 @@ EditorProfiler::EditorProfiler() {
hb_time->add_child(memnew(Label(TTR("Time:"))));
display_time = memnew(OptionButton);
display_time->set_accessibility_name(TTRC("Time"));
// TRANSLATORS: This is an option in the profiler to display the time spent in a function, including the time spent in other functions called by that function.
display_time->add_item(TTR("Inclusive"));
// TRANSLATORS: This is an option in the profiler to display the time spent in a function, exincluding the time spent in other functions called by that function.
@ -731,6 +733,7 @@ EditorProfiler::EditorProfiler() {
hb_frame->add_child(memnew(Label(TTR("Frame #:"))));
cursor_metric_edit = memnew(SpinBox);
cursor_metric_edit->set_accessibility_name(TTRC("Frame"));
cursor_metric_edit->set_h_size_flags(SIZE_FILL);
cursor_metric_edit->set_value(0);
cursor_metric_edit->set_editable(false);

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_PROFILER_H
#define EDITOR_PROFILER_H
#pragma once
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
@ -186,5 +185,3 @@ public:
EditorProfiler();
};
#endif // EDITOR_PROFILER_H

View file

@ -136,7 +136,7 @@ String EditorVisualProfiler::_get_time_as_text(float p_time) {
Color EditorVisualProfiler::_get_color_from_signature(const StringName &p_signature) const {
Color bc = get_theme_color(SNAME("error_color"), EditorStringName(Editor));
double rot = ABS(double(p_signature.hash()) / double(0x7FFFFFFF));
double rot = Math::abs(double(p_signature.hash()) / double(0x7FFFFFFF));
Color c;
c.set_hsv(rot, bc.get_s(), bc.get_v());
return c.lerp(get_theme_color(SNAME("base_color"), EditorStringName(Editor)), 0.07);
@ -362,7 +362,7 @@ void EditorVisualProfiler::_update_frame(bool p_focus_selected) {
stack.push_back(category);
categories.push_back(category);
name = name.substr(1, name.length());
name = name.substr(1);
category->set_text(0, name);
category->set_metadata(1, cpu_time);
@ -778,6 +778,7 @@ EditorVisualProfiler::EditorVisualProfiler() {
hb_measure->add_child(memnew(Label(TTR("Measure:"))));
display_mode = memnew(OptionButton);
display_mode->set_accessibility_name(TTRC("Measure"));
display_mode->add_item(TTR("Frame Time (ms)"));
display_mode->add_item(TTR("Frame %"));
display_mode->connect(SceneStringName(item_selected), callable_mp(this, &EditorVisualProfiler::_combo_changed));
@ -801,6 +802,7 @@ EditorVisualProfiler::EditorVisualProfiler() {
hb_frame->add_child(memnew(Label(TTR("Frame #:"))));
cursor_metric_edit = memnew(SpinBox);
cursor_metric_edit->set_accessibility_name(TTRC("Frame"));
cursor_metric_edit->set_h_size_flags(SIZE_FILL);
hb_frame->add_child(cursor_metric_edit);
cursor_metric_edit->connect(SceneStringName(value_changed), callable_mp(this, &EditorVisualProfiler::_cursor_metric_changed));

View file

@ -28,8 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef EDITOR_VISUAL_PROFILER_H
#define EDITOR_VISUAL_PROFILER_H
#pragma once
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
@ -153,5 +152,3 @@ public:
EditorVisualProfiler();
};
#endif // EDITOR_VISUAL_PROFILER_H

File diff suppressed because it is too large Load diff

View file

@ -28,17 +28,15 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef SCRIPT_EDITOR_DEBUGGER_H
#define SCRIPT_EDITOR_DEBUGGER_H
#pragma once
#include "core/object/script_language.h"
#include "core/os/os.h"
#include "editor/debugger/editor_debugger_inspector.h"
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/editor_debugger_server.h"
#include "scene/gui/button.h"
#include "scene/gui/margin_container.h"
class Button;
class Tree;
class LineEdit;
class TabContainer;
@ -115,6 +113,7 @@ private:
int warning_count;
bool skip_breakpoints_value = false;
bool ignore_error_breaks_value = false;
Ref<Script> stack_script;
TabContainer *tabs = nullptr;
@ -122,6 +121,7 @@ private:
Label *reason = nullptr;
Button *skip_breakpoints = nullptr;
Button *ignore_error_breaks = nullptr;
Button *copy = nullptr;
Button *step = nullptr;
Button *next = nullptr;
@ -147,7 +147,7 @@ private:
Ref<RemoteDebuggerPeer> peer;
HashMap<NodePath, int> node_path_cache;
int last_path_id;
int last_path_id = 0;
HashMap<String, int> res_path_cache;
EditorProfiler *profiler = nullptr;
@ -159,7 +159,7 @@ private:
bool move_to_foreground = true;
bool can_request_idle_draw = false;
bool live_debug;
bool live_debug = true;
uint64_t debugging_thread_id = Thread::UNASSIGNED_ID;
@ -183,16 +183,54 @@ private:
void _select_thread(int p_index);
bool debug_mute_audio = false;
EditorDebuggerNode::CameraOverride camera_override;
void _stack_dump_frame_selected();
void _file_selected(const String &p_file);
/// Message handler function for _parse_message.
typedef void (ScriptEditorDebugger::*ParseMessageFunc)(uint64_t p_thread_id, const Array &p_data);
static HashMap<String, ParseMessageFunc> parse_message_handlers;
static void _init_parse_message_handlers();
void _msg_debug_enter(uint64_t p_thread_id, const Array &p_data);
void _msg_debug_exit(uint64_t p_thread_id, const Array &p_data);
void _msg_set_pid(uint64_t p_thread_id, const Array &p_data);
void _msg_scene_click_ctrl(uint64_t p_thread_id, const Array &p_data);
void _msg_scene_scene_tree(uint64_t p_thread_id, const Array &p_data);
void _msg_scene_inspect_objects(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_memory_usage(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_drawn(uint64_t p_thread_id, const Array &p_data);
void _msg_stack_dump(uint64_t p_thread_id, const Array &p_data);
void _msg_stack_frame_vars(uint64_t p_thread_id, const Array &p_data);
void _msg_stack_frame_var(uint64_t p_thread_id, const Array &p_data);
void _msg_output(uint64_t p_thread_id, const Array &p_data);
void _msg_performance_profile_frame(uint64_t p_thread_id, const Array &p_data);
void _msg_visual_hardware_info(uint64_t p_thread_id, const Array &p_data);
void _msg_visual_profile_frame(uint64_t p_thread_id, const Array &p_data);
void _msg_error(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_function_signature(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_profile_common(const Array &p_data, const bool p_final);
void _msg_servers_profile_frame(uint64_t p_thread_id, const Array &p_data);
void _msg_servers_profile_total(uint64_t p_thread_id, const Array &p_data);
void _msg_request_quit(uint64_t p_thread_id, const Array &p_data);
void _msg_remote_objects_selected(uint64_t p_thread_id, const Array &p_data);
void _msg_remote_nothing_selected(uint64_t p_thread_id, const Array &p_data);
void _msg_remote_selection_invalidated(uint64_t p_thread_id, const Array &p_data);
void _msg_show_selection_limit_warning(uint64_t p_thread_id, const Array &p_data);
void _msg_performance_profile_names(uint64_t p_thread_id, const Array &p_data);
void _msg_filesystem_update_file(uint64_t p_thread_id, const Array &p_data);
void _msg_evaluation_return(uint64_t p_thread_id, const Array &p_data);
void _msg_window_title(uint64_t p_thread_id, const Array &p_data);
void _parse_message(const String &p_msg, uint64_t p_thread_id, const Array &p_data);
void _set_reason_text(const String &p_reason, MessageType p_type);
void _update_buttons_state();
void _remote_object_selected(ObjectID p_object);
void _remote_object_edited(ObjectID, const String &p_prop, const Variant &p_value);
void _remote_objects_edited(const String &p_prop, const TypedDictionary<uint64_t, Variant> &p_values, const String &p_field);
void _remote_object_property_updated(ObjectID p_id, const String &p_property);
void _video_mem_request();
@ -216,6 +254,8 @@ private:
void _expand_errors_list();
void _collapse_errors_list();
void _vmem_item_activated();
void _profiler_activate(bool p_enable, int p_profiler);
void _profiler_seeked();
@ -246,9 +286,10 @@ protected:
static void _bind_methods();
public:
void request_remote_object(ObjectID p_obj_id);
void update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value);
Object *get_remote_object(ObjectID p_id);
void request_remote_objects(const TypedArray<uint64_t> &p_obj_ids, bool p_update_selection = true);
void update_remote_object(ObjectID p_obj_id, const String &p_prop, const Variant &p_value, const String &p_field = "");
void clear_inspector(bool p_send_msg = true);
// Needed by _live_edit_set, buttons state.
void set_editor_remote_tree(const Tree *p_tree) { editor_remote_tree = p_tree; }
@ -262,6 +303,7 @@ public:
void stop();
void debug_skip_breakpoints();
void debug_ignore_error_breaks();
void debug_copy();
void debug_next();
@ -299,6 +341,9 @@ public:
void live_debug_duplicate_node(const NodePath &p_at, const String &p_new_name);
void live_debug_reparent_node(const NodePath &p_at, const NodePath &p_new_place, const String &p_new_name, int p_at_pos);
bool get_debug_mute_audio() const;
void set_debug_mute_audio(bool p_mute);
EditorDebuggerNode::CameraOverride get_camera_override() const;
void set_camera_override(EditorDebuggerNode::CameraOverride p_override);
@ -309,7 +354,8 @@ public:
void reload_all_scripts();
void reload_scripts(const Vector<String> &p_script_paths);
bool is_skip_breakpoints();
bool is_skip_breakpoints() const;
bool is_ignore_error_breaks() const;
virtual Size2 get_minimum_size() const override;
@ -324,5 +370,3 @@ public:
ScriptEditorDebugger();
~ScriptEditorDebugger();
};
#endif // SCRIPT_EDITOR_DEBUGGER_H