Path: blob/master/modules/mono/editor/code_completion.cpp
10279 views
/**************************************************************************/1/* code_completion.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 "code_completion.h"3132#include "core/config/project_settings.h"33#include "core/object/script_language.h"34#include "editor/file_system/editor_file_system.h"35#include "editor/settings/editor_settings.h"36#include "scene/gui/control.h"37#include "scene/main/node.h"38#include "scene/theme/theme_db.h"3940namespace gdmono {4142// Almost everything here is taken from functions used by GDScript for code completion, adapted for C#.4344_FORCE_INLINE_ String quoted(const String &p_str) {45return "\"" + p_str + "\"";46}4748void _add_nodes_suggestions(const Node *p_base, const Node *p_node, PackedStringArray &r_suggestions) {49if (p_node != p_base && !p_node->get_owner()) {50return;51}5253String path_relative_to_orig = String(p_base->get_path_to(p_node));5455r_suggestions.push_back(quoted(path_relative_to_orig));5657for (int i = 0; i < p_node->get_child_count(); i++) {58_add_nodes_suggestions(p_base, p_node->get_child(i), r_suggestions);59}60}6162Node *_find_node_for_script(Node *p_base, Node *p_current, const Ref<Script> &p_script) {63if (p_current->get_owner() != p_base && p_base != p_current) {64return nullptr;65}6667Ref<Script> c = p_current->get_script();6869if (c == p_script) {70return p_current;71}7273for (int i = 0; i < p_current->get_child_count(); i++) {74Node *found = _find_node_for_script(p_base, p_current->get_child(i), p_script);75if (found) {76return found;77}78}7980return nullptr;81}8283void _get_directory_contents(EditorFileSystemDirectory *p_dir, PackedStringArray &r_suggestions) {84for (int i = 0; i < p_dir->get_file_count(); i++) {85r_suggestions.push_back(quoted(p_dir->get_file_path(i)));86}8788for (int i = 0; i < p_dir->get_subdir_count(); i++) {89_get_directory_contents(p_dir->get_subdir(i), r_suggestions);90}91}9293Node *_try_find_owner_node_in_tree(const Ref<Script> p_script) {94SceneTree *tree = SceneTree::get_singleton();95if (!tree) {96return nullptr;97}98Node *base = tree->get_edited_scene_root();99if (base) {100base = _find_node_for_script(base, base, p_script);101}102return base;103}104105PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_script_file) {106PackedStringArray suggestions;107108switch (p_kind) {109case CompletionKind::INPUT_ACTIONS: {110List<PropertyInfo> project_props;111ProjectSettings::get_singleton()->get_property_list(&project_props);112113for (const PropertyInfo &prop : project_props) {114if (!prop.name.begins_with("input/")) {115continue;116}117118String name = prop.name.substr(prop.name.find_char('/') + 1);119suggestions.push_back(quoted(name));120}121} break;122case CompletionKind::NODE_PATHS: {123{124// Autoloads.125HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();126127for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {128const ProjectSettings::AutoloadInfo &info = E.value;129suggestions.push_back(quoted("/root/" + String(info.name)));130}131}132133{134// Current edited scene tree135Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());136Node *base = _try_find_owner_node_in_tree(script);137if (base) {138_add_nodes_suggestions(base, base, suggestions);139}140}141} break;142case CompletionKind::RESOURCE_PATHS: {143if (bool(EDITOR_GET("text_editor/completion/complete_file_paths"))) {144_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), suggestions);145}146} break;147case CompletionKind::SCENE_PATHS: {148Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);149List<String> directories;150directories.push_back(dir_access->get_current_dir());151152while (!directories.is_empty()) {153dir_access->change_dir(directories.back()->get());154directories.pop_back();155156dir_access->list_dir_begin();157String filename = dir_access->get_next();158159while (!filename.is_empty()) {160if (filename == "." || filename == "..") {161filename = dir_access->get_next();162continue;163}164165if (dir_access->dir_exists(filename)) {166directories.push_back(dir_access->get_current_dir().path_join(filename));167} else if (filename.ends_with(".tscn") || filename.ends_with(".scn")) {168suggestions.push_back(quoted(dir_access->get_current_dir().path_join(filename)));169}170171filename = dir_access->get_next();172}173}174} break;175case CompletionKind::SHADER_PARAMS: {176print_verbose("Shader uniforms completion for C# is not implemented yet.");177} break;178case CompletionKind::SIGNALS: {179Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());180181List<MethodInfo> signals;182script->get_script_signal_list(&signals);183184StringName native = script->get_instance_base_type();185if (native != StringName()) {186ClassDB::get_signal_list(native, &signals, /* p_no_inheritance: */ false);187}188189for (const MethodInfo &E : signals) {190const String &signal = E.name;191suggestions.push_back(quoted(signal));192}193} break;194case CompletionKind::THEME_COLORS: {195Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());196Node *base = _try_find_owner_node_in_tree(script);197if (base && Object::cast_to<Control>(base)) {198List<StringName> sn;199ThemeDB::get_singleton()->get_default_theme()->get_color_list(base->get_class(), &sn);200201for (const StringName &E : sn) {202suggestions.push_back(quoted(E));203}204}205} break;206case CompletionKind::THEME_CONSTANTS: {207Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());208Node *base = _try_find_owner_node_in_tree(script);209if (base && Object::cast_to<Control>(base)) {210List<StringName> sn;211ThemeDB::get_singleton()->get_default_theme()->get_constant_list(base->get_class(), &sn);212213for (const StringName &E : sn) {214suggestions.push_back(quoted(E));215}216}217} break;218case CompletionKind::THEME_FONTS: {219Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());220Node *base = _try_find_owner_node_in_tree(script);221if (base && Object::cast_to<Control>(base)) {222List<StringName> sn;223ThemeDB::get_singleton()->get_default_theme()->get_font_list(base->get_class(), &sn);224225for (const StringName &E : sn) {226suggestions.push_back(quoted(E));227}228}229} break;230case CompletionKind::THEME_FONT_SIZES: {231Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());232Node *base = _try_find_owner_node_in_tree(script);233if (base && Object::cast_to<Control>(base)) {234List<StringName> sn;235ThemeDB::get_singleton()->get_default_theme()->get_font_size_list(base->get_class(), &sn);236237for (const StringName &E : sn) {238suggestions.push_back(quoted(E));239}240}241} break;242case CompletionKind::THEME_STYLES: {243Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());244Node *base = _try_find_owner_node_in_tree(script);245if (base && Object::cast_to<Control>(base)) {246List<StringName> sn;247ThemeDB::get_singleton()->get_default_theme()->get_stylebox_list(base->get_class(), &sn);248249for (const StringName &E : sn) {250suggestions.push_back(quoted(E));251}252}253} break;254default:255ERR_FAIL_V_MSG(suggestions, "Invalid completion kind.");256}257258return suggestions;259}260} // namespace gdmono261262263