Path: blob/master/modules/gdscript/gdscript_resource_format.cpp
45997 views
/**************************************************************************/1/* gdscript_resource_format.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "gdscript_resource_format.h"3132#include "gdscript_cache.h"33#include "gdscript_parser.h"3435#include "core/io/file_access.h"36#include "core/object/class_db.h"3738Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {39Error err;40bool ignoring = p_cache_mode == CACHE_MODE_IGNORE || p_cache_mode == CACHE_MODE_IGNORE_DEEP;41Ref<GDScript> scr = GDScriptCache::get_full_script(p_original_path, err, "", ignoring);4243if (err && scr.is_valid()) {44// If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error.45ERR_PRINT_ED(vformat(R"(Failed to load script "%s" with error "%s".)", p_original_path, error_names[err]));46}4748if (r_error) {49// Don't fail loading because of parsing error.50*r_error = scr.is_valid() ? OK : err;51}5253return scr;54}5556void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {57p_extensions->push_back("gd");58p_extensions->push_back("gdc");59}6061bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {62return (p_type == "Script" || p_type == "GDScript");63}6465String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const {66String el = p_path.get_extension().to_lower();67if (el == "gd" || el == "gdc") {68return "GDScript";69}70return "";71}7273void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) {74Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ);75ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_path + "'.");7677String source = file->get_as_utf8_string();78if (source.is_empty()) {79return;80}8182GDScriptParser parser;83if (OK != parser.parse(source, p_path, false)) {84return;85}8687for (const String &E : parser.get_dependencies()) {88p_dependencies->push_back(E);89}90}9192void ResourceFormatLoaderGDScript::get_classes_used(const String &p_path, HashSet<StringName> *r_classes) {93Ref<GDScript> scr = ResourceLoader::load(p_path);94if (scr.is_null()) {95return;96}9798const String source = scr->get_source_code();99GDScriptTokenizerText tokenizer;100tokenizer.set_source_code(source);101GDScriptTokenizer::Token current = tokenizer.scan();102while (current.type != GDScriptTokenizer::Token::TK_EOF) {103if (!current.is_identifier()) {104current = tokenizer.scan();105continue;106}107108int insert_idx = 0;109for (int i = 0; i < current.start_line - 1; i++) {110insert_idx = source.find("\n", insert_idx) + 1;111}112// Insert the "cursor" character, needed for the lookup to work.113const String source_with_cursor = source.insert(insert_idx + current.start_column, String::chr(0xFFFF));114115ScriptLanguage::LookupResult result;116if (scr->get_language()->lookup_code(source_with_cursor, current.get_identifier(), p_path, nullptr, result) == OK) {117if (!result.class_name.is_empty() && ClassDB::class_exists(result.class_name)) {118r_classes->insert(result.class_name);119}120121if (result.type == ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY) {122PropertyInfo prop;123if (ClassDB::get_property_info(result.class_name, result.class_member, &prop)) {124if (!prop.class_name.is_empty() && ClassDB::class_exists(prop.class_name)) {125r_classes->insert(prop.class_name);126}127if (!prop.hint_string.is_empty() && ClassDB::class_exists(prop.hint_string)) {128r_classes->insert(prop.hint_string);129}130}131} else if (result.type == ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD) {132MethodInfo met;133if (ClassDB::get_method_info(result.class_name, result.class_member, &met)) {134if (!met.return_val.class_name.is_empty() && ClassDB::class_exists(met.return_val.class_name)) {135r_classes->insert(met.return_val.class_name);136}137if (!met.return_val.hint_string.is_empty() && ClassDB::class_exists(met.return_val.hint_string)) {138r_classes->insert(met.return_val.hint_string);139}140}141}142}143144current = tokenizer.scan();145}146}147148Error ResourceFormatSaverGDScript::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {149Ref<GDScript> sqscr = p_resource;150ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);151152String source = sqscr->get_source_code();153154{155Error err;156Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);157158ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'.");159160file->store_string(source);161if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {162return ERR_CANT_CREATE;163}164}165166if (ScriptServer::is_reload_scripts_on_save_enabled()) {167GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, true);168}169170return OK;171}172173void ResourceFormatSaverGDScript::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {174if (Object::cast_to<GDScript>(*p_resource)) {175p_extensions->push_back("gd");176}177}178179bool ResourceFormatSaverGDScript::recognize(const Ref<Resource> &p_resource) const {180return Object::cast_to<GDScript>(*p_resource) != nullptr;181}182183184