diff --git a/core/object/script_language.h b/core/object/script_language.h index 2b685c77a3e4..3ea6a6e4c3c0 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -263,6 +263,7 @@ class ScriptLanguage : public Object { }; struct ScriptError { + String path; int line = -1; int column = -1; String message; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 0a1f735f64e6..bc900e377d08 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -282,6 +282,22 @@ void ScriptTextEditor::_error_clicked(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()); + } 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); + } else { + Ref scr = ResourceLoader::load(path); + if (!scr.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("Could not load file at:") + "\n\n" + path, TTR("Error!")); + } else { + ScriptEditor::get_singleton()->edit(scr, line, column); + } + } } } @@ -458,9 +474,17 @@ void ScriptTextEditor::_validate_script() { warnings.clear(); errors.clear(); + depended_errors.clear(); safe_lines.clear(); if (!script->get_language()->validate(text, script->get_path(), &fnc, &errors, &warnings, &safe_lines)) { + for (List::Element *E = errors.front(); E; E = E->next()) { + if (E->get().path.is_empty() || E->get().path != script->get_path()) { + depended_errors[E->get().path].push_back(E->get()); + E->erase(); + } + } + // TRANSLATORS: Script error pointing to a line and column number. String error_text = vformat(TTR("Error at (%d, %d):"), errors[0].line, errors[0].column) + " " + errors[0].message; code_editor->set_error(error_text); @@ -560,6 +584,10 @@ void ScriptTextEditor::_update_errors() { errors_panel->clear(); errors_panel->push_table(2); for (const ScriptLanguage::ScriptError &err : errors) { + Dictionary click_meta; + click_meta["line"] = err.line; + click_meta["column"] = err.column; + errors_panel->push_cell(); errors_panel->push_meta(err.line - 1); errors_panel->push_color(warnings_panel->get_theme_color(SNAME("error_color"), SNAME("Editor"))); @@ -575,6 +603,41 @@ void ScriptTextEditor::_update_errors() { } errors_panel->pop(); // Table + for (const KeyValue> &KV : depended_errors) { + Dictionary click_meta; + click_meta["path"] = KV.key; + click_meta["line"] = 1; + + errors_panel->add_newline(); + errors_panel->add_newline(); + errors_panel->push_meta(click_meta); + errors_panel->add_text(vformat(R"(%s:)", KV.key)); + errors_panel->pop(); // Meta goto. + errors_panel->add_newline(); + + errors_panel->push_indent(1); + errors_panel->push_table(2); + String filename = KV.key.get_file(); + for (const ScriptLanguage::ScriptError &err : KV.value) { + click_meta["line"] = err.line; + click_meta["column"] = err.column; + + errors_panel->push_cell(); + errors_panel->push_meta(click_meta); + errors_panel->push_color(errors_panel->get_theme_color(SNAME("error_color"), SNAME("Editor"))); + errors_panel->add_text(TTR("Line") + " " + itos(err.line) + ":"); + errors_panel->pop(); // Color. + errors_panel->pop(); // Meta goto. + errors_panel->pop(); // Cell. + + errors_panel->push_cell(); + errors_panel->add_text(err.message); + errors_panel->pop(); // Cell. + } + errors_panel->pop(); // Table + errors_panel->pop(); // Indent. + } + CodeEdit *te = code_editor->get_text_editor(); bool highlight_safe = EDITOR_GET("text_editor/appearance/gutters/highlight_type_safe_lines"); bool last_is_safe = false; diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 808a6417e4b9..d275013b9165 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -68,6 +68,7 @@ class ScriptTextEditor : public ScriptEditorBase { Vector functions; List warnings; List errors; + HashMap> depended_errors; HashSet safe_lines; List missing_connections; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index d3445b8cc0c8..78337a6245cc 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -4517,6 +4517,10 @@ Variant GDScriptAnalyzer::make_variable_default_value(GDScriptParser::VariableNo return result; } +const HashMap> &GDScriptAnalyzer::get_depended_parsers() { + return depended_parsers; +} + GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) { GDScriptParser::DataType result; result.is_constant = true; diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 195e23b503bb..4b6b0fbe26a5 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -144,6 +144,7 @@ class GDScriptAnalyzer { Error analyze(); Variant make_variable_default_value(GDScriptParser::VariableNode *p_variable); + const HashMap> &get_depended_parsers(); GDScriptAnalyzer(GDScriptParser *p_parser); }; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index cd34feb8b319..79aba4f1a38c 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -143,14 +143,26 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li #endif if (err) { if (r_errors) { - for (const GDScriptParser::ParserError &E : parser.get_errors()) { - const GDScriptParser::ParserError &pe = E; + for (const GDScriptParser::ParserError &pe : parser.get_errors()) { ScriptLanguage::ScriptError e; + e.path = p_path; e.line = pe.line; e.column = pe.column; e.message = pe.message; r_errors->push_back(e); } + + for (KeyValue> E : analyzer.get_depended_parsers()) { + GDScriptParser *depended_parser = E.value->get_parser(); + for (const GDScriptParser::ParserError &pe : depended_parser->get_errors()) { + ScriptLanguage::ScriptError e; + e.path = E.key; + e.line = pe.line; + e.column = pe.column; + e.message = pe.message; + r_errors->push_back(e); + } + } } return false; } else {