Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/mono/editor/code_completion.cpp
10279 views
1
/**************************************************************************/
2
/* code_completion.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "code_completion.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/object/script_language.h"
35
#include "editor/file_system/editor_file_system.h"
36
#include "editor/settings/editor_settings.h"
37
#include "scene/gui/control.h"
38
#include "scene/main/node.h"
39
#include "scene/theme/theme_db.h"
40
41
namespace gdmono {
42
43
// Almost everything here is taken from functions used by GDScript for code completion, adapted for C#.
44
45
_FORCE_INLINE_ String quoted(const String &p_str) {
46
return "\"" + p_str + "\"";
47
}
48
49
void _add_nodes_suggestions(const Node *p_base, const Node *p_node, PackedStringArray &r_suggestions) {
50
if (p_node != p_base && !p_node->get_owner()) {
51
return;
52
}
53
54
String path_relative_to_orig = String(p_base->get_path_to(p_node));
55
56
r_suggestions.push_back(quoted(path_relative_to_orig));
57
58
for (int i = 0; i < p_node->get_child_count(); i++) {
59
_add_nodes_suggestions(p_base, p_node->get_child(i), r_suggestions);
60
}
61
}
62
63
Node *_find_node_for_script(Node *p_base, Node *p_current, const Ref<Script> &p_script) {
64
if (p_current->get_owner() != p_base && p_base != p_current) {
65
return nullptr;
66
}
67
68
Ref<Script> c = p_current->get_script();
69
70
if (c == p_script) {
71
return p_current;
72
}
73
74
for (int i = 0; i < p_current->get_child_count(); i++) {
75
Node *found = _find_node_for_script(p_base, p_current->get_child(i), p_script);
76
if (found) {
77
return found;
78
}
79
}
80
81
return nullptr;
82
}
83
84
void _get_directory_contents(EditorFileSystemDirectory *p_dir, PackedStringArray &r_suggestions) {
85
for (int i = 0; i < p_dir->get_file_count(); i++) {
86
r_suggestions.push_back(quoted(p_dir->get_file_path(i)));
87
}
88
89
for (int i = 0; i < p_dir->get_subdir_count(); i++) {
90
_get_directory_contents(p_dir->get_subdir(i), r_suggestions);
91
}
92
}
93
94
Node *_try_find_owner_node_in_tree(const Ref<Script> p_script) {
95
SceneTree *tree = SceneTree::get_singleton();
96
if (!tree) {
97
return nullptr;
98
}
99
Node *base = tree->get_edited_scene_root();
100
if (base) {
101
base = _find_node_for_script(base, base, p_script);
102
}
103
return base;
104
}
105
106
PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_script_file) {
107
PackedStringArray suggestions;
108
109
switch (p_kind) {
110
case CompletionKind::INPUT_ACTIONS: {
111
List<PropertyInfo> project_props;
112
ProjectSettings::get_singleton()->get_property_list(&project_props);
113
114
for (const PropertyInfo &prop : project_props) {
115
if (!prop.name.begins_with("input/")) {
116
continue;
117
}
118
119
String name = prop.name.substr(prop.name.find_char('/') + 1);
120
suggestions.push_back(quoted(name));
121
}
122
} break;
123
case CompletionKind::NODE_PATHS: {
124
{
125
// Autoloads.
126
HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
127
128
for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {
129
const ProjectSettings::AutoloadInfo &info = E.value;
130
suggestions.push_back(quoted("/root/" + String(info.name)));
131
}
132
}
133
134
{
135
// Current edited scene tree
136
Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
137
Node *base = _try_find_owner_node_in_tree(script);
138
if (base) {
139
_add_nodes_suggestions(base, base, suggestions);
140
}
141
}
142
} break;
143
case CompletionKind::RESOURCE_PATHS: {
144
if (bool(EDITOR_GET("text_editor/completion/complete_file_paths"))) {
145
_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), suggestions);
146
}
147
} break;
148
case CompletionKind::SCENE_PATHS: {
149
Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_RESOURCES);
150
List<String> directories;
151
directories.push_back(dir_access->get_current_dir());
152
153
while (!directories.is_empty()) {
154
dir_access->change_dir(directories.back()->get());
155
directories.pop_back();
156
157
dir_access->list_dir_begin();
158
String filename = dir_access->get_next();
159
160
while (!filename.is_empty()) {
161
if (filename == "." || filename == "..") {
162
filename = dir_access->get_next();
163
continue;
164
}
165
166
if (dir_access->dir_exists(filename)) {
167
directories.push_back(dir_access->get_current_dir().path_join(filename));
168
} else if (filename.ends_with(".tscn") || filename.ends_with(".scn")) {
169
suggestions.push_back(quoted(dir_access->get_current_dir().path_join(filename)));
170
}
171
172
filename = dir_access->get_next();
173
}
174
}
175
} break;
176
case CompletionKind::SHADER_PARAMS: {
177
print_verbose("Shader uniforms completion for C# is not implemented yet.");
178
} break;
179
case CompletionKind::SIGNALS: {
180
Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
181
182
List<MethodInfo> signals;
183
script->get_script_signal_list(&signals);
184
185
StringName native = script->get_instance_base_type();
186
if (native != StringName()) {
187
ClassDB::get_signal_list(native, &signals, /* p_no_inheritance: */ false);
188
}
189
190
for (const MethodInfo &E : signals) {
191
const String &signal = E.name;
192
suggestions.push_back(quoted(signal));
193
}
194
} break;
195
case CompletionKind::THEME_COLORS: {
196
Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
197
Node *base = _try_find_owner_node_in_tree(script);
198
if (base && Object::cast_to<Control>(base)) {
199
List<StringName> sn;
200
ThemeDB::get_singleton()->get_default_theme()->get_color_list(base->get_class(), &sn);
201
202
for (const StringName &E : sn) {
203
suggestions.push_back(quoted(E));
204
}
205
}
206
} break;
207
case CompletionKind::THEME_CONSTANTS: {
208
Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
209
Node *base = _try_find_owner_node_in_tree(script);
210
if (base && Object::cast_to<Control>(base)) {
211
List<StringName> sn;
212
ThemeDB::get_singleton()->get_default_theme()->get_constant_list(base->get_class(), &sn);
213
214
for (const StringName &E : sn) {
215
suggestions.push_back(quoted(E));
216
}
217
}
218
} break;
219
case CompletionKind::THEME_FONTS: {
220
Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
221
Node *base = _try_find_owner_node_in_tree(script);
222
if (base && Object::cast_to<Control>(base)) {
223
List<StringName> sn;
224
ThemeDB::get_singleton()->get_default_theme()->get_font_list(base->get_class(), &sn);
225
226
for (const StringName &E : sn) {
227
suggestions.push_back(quoted(E));
228
}
229
}
230
} break;
231
case CompletionKind::THEME_FONT_SIZES: {
232
Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
233
Node *base = _try_find_owner_node_in_tree(script);
234
if (base && Object::cast_to<Control>(base)) {
235
List<StringName> sn;
236
ThemeDB::get_singleton()->get_default_theme()->get_font_size_list(base->get_class(), &sn);
237
238
for (const StringName &E : sn) {
239
suggestions.push_back(quoted(E));
240
}
241
}
242
} break;
243
case CompletionKind::THEME_STYLES: {
244
Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
245
Node *base = _try_find_owner_node_in_tree(script);
246
if (base && Object::cast_to<Control>(base)) {
247
List<StringName> sn;
248
ThemeDB::get_singleton()->get_default_theme()->get_stylebox_list(base->get_class(), &sn);
249
250
for (const StringName &E : sn) {
251
suggestions.push_back(quoted(E));
252
}
253
}
254
} break;
255
default:
256
ERR_FAIL_V_MSG(suggestions, "Invalid completion kind.");
257
}
258
259
return suggestions;
260
}
261
} // namespace gdmono
262
263