1 /*************************************************************************/
2 /*  script_editor_plugin.cpp                                             */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
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 "script_editor_plugin.h"
32 
33 #include "core/io/resource_loader.h"
34 #include "core/os/file_access.h"
35 #include "core/os/input.h"
36 #include "core/os/keyboard.h"
37 #include "core/os/os.h"
38 #include "core/project_settings.h"
39 #include "editor/editor_node.h"
40 #include "editor/editor_run_script.h"
41 #include "editor/editor_scale.h"
42 #include "editor/editor_settings.h"
43 #include "editor/filesystem_dock.h"
44 #include "editor/find_in_files.h"
45 #include "editor/node_dock.h"
46 #include "editor/plugins/shader_editor_plugin.h"
47 #include "editor/script_editor_debugger.h"
48 #include "scene/main/viewport.h"
49 #include "scene/scene_string_names.h"
50 #include "script_text_editor.h"
51 #include "text_editor.h"
52 
53 /*** SCRIPT EDITOR ****/
54 
_bind_methods()55 void ScriptEditorBase::_bind_methods() {
56 
57 	ADD_SIGNAL(MethodInfo("name_changed"));
58 	ADD_SIGNAL(MethodInfo("edited_script_changed"));
59 	ADD_SIGNAL(MethodInfo("request_help", PropertyInfo(Variant::STRING, "topic")));
60 	ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line")));
61 	ADD_SIGNAL(MethodInfo("request_save_history"));
62 	ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what")));
63 	// TODO: This signal is no use for VisualScript.
64 	ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text")));
65 }
66 
_is_built_in_script(Script * p_script)67 static bool _is_built_in_script(Script *p_script) {
68 	String path = p_script->get_path();
69 
70 	return path.find("::") != -1;
71 }
72 
73 class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache {
74 
75 	struct Cache {
76 		uint64_t time_loaded;
77 		RES cache;
78 	};
79 
80 	Map<String, Cache> cached;
81 
82 public:
83 	uint64_t max_time_cache;
84 	int max_cache_size;
85 
cleanup()86 	void cleanup() {
87 
88 		List<Map<String, Cache>::Element *> to_clean;
89 
90 		Map<String, Cache>::Element *I = cached.front();
91 		while (I) {
92 			if ((OS::get_singleton()->get_ticks_msec() - I->get().time_loaded) > max_time_cache) {
93 				to_clean.push_back(I);
94 			}
95 			I = I->next();
96 		}
97 
98 		while (to_clean.front()) {
99 			cached.erase(to_clean.front()->get());
100 			to_clean.pop_front();
101 		}
102 	}
103 
get_cached_resource(const String & p_path)104 	virtual RES get_cached_resource(const String &p_path) {
105 
106 		Map<String, Cache>::Element *E = cached.find(p_path);
107 		if (!E) {
108 
109 			Cache c;
110 			c.cache = ResourceLoader::load(p_path);
111 			E = cached.insert(p_path, c);
112 		}
113 
114 		E->get().time_loaded = OS::get_singleton()->get_ticks_msec();
115 
116 		if (cached.size() > max_cache_size) {
117 			uint64_t older;
118 			Map<String, Cache>::Element *O = cached.front();
119 			older = O->get().time_loaded;
120 			Map<String, Cache>::Element *I = O;
121 			while (I) {
122 				if (I->get().time_loaded < older) {
123 					older = I->get().time_loaded;
124 					O = I;
125 				}
126 				I = I->next();
127 			}
128 
129 			if (O != E) { //should never happen..
130 				cached.erase(O);
131 			}
132 		}
133 
134 		return E->get().cache;
135 	}
136 
EditorScriptCodeCompletionCache()137 	EditorScriptCodeCompletionCache() {
138 
139 		max_cache_size = 128;
140 		max_time_cache = 5 * 60 * 1000; //minutes, five
141 	}
142 
~EditorScriptCodeCompletionCache()143 	virtual ~EditorScriptCodeCompletionCache() {}
144 };
145 
popup_dialog(const Vector<String> & p_functions,bool p_dontclear)146 void ScriptEditorQuickOpen::popup_dialog(const Vector<String> &p_functions, bool p_dontclear) {
147 
148 	popup_centered_ratio(0.6);
149 	if (p_dontclear)
150 		search_box->select_all();
151 	else
152 		search_box->clear();
153 	search_box->grab_focus();
154 	functions = p_functions;
155 	_update_search();
156 }
157 
_text_changed(const String & p_newtext)158 void ScriptEditorQuickOpen::_text_changed(const String &p_newtext) {
159 
160 	_update_search();
161 }
162 
_sbox_input(const Ref<InputEvent> & p_ie)163 void ScriptEditorQuickOpen::_sbox_input(const Ref<InputEvent> &p_ie) {
164 
165 	Ref<InputEventKey> k = p_ie;
166 
167 	if (k.is_valid() && (k->get_scancode() == KEY_UP ||
168 								k->get_scancode() == KEY_DOWN ||
169 								k->get_scancode() == KEY_PAGEUP ||
170 								k->get_scancode() == KEY_PAGEDOWN)) {
171 
172 		search_options->call("_gui_input", k);
173 		search_box->accept_event();
174 	}
175 }
176 
_update_search()177 void ScriptEditorQuickOpen::_update_search() {
178 
179 	search_options->clear();
180 	TreeItem *root = search_options->create_item();
181 
182 	for (int i = 0; i < functions.size(); i++) {
183 
184 		String file = functions[i];
185 		if ((search_box->get_text() == "" || file.findn(search_box->get_text()) != -1)) {
186 
187 			TreeItem *ti = search_options->create_item(root);
188 			ti->set_text(0, file);
189 			if (root->get_children() == ti)
190 				ti->select(0);
191 		}
192 	}
193 
194 	get_ok()->set_disabled(root->get_children() == NULL);
195 }
196 
_confirmed()197 void ScriptEditorQuickOpen::_confirmed() {
198 
199 	TreeItem *ti = search_options->get_selected();
200 	if (!ti)
201 		return;
202 	int line = ti->get_text(0).get_slice(":", 1).to_int();
203 
204 	emit_signal("goto_line", line - 1);
205 	hide();
206 }
207 
_notification(int p_what)208 void ScriptEditorQuickOpen::_notification(int p_what) {
209 
210 	switch (p_what) {
211 		case NOTIFICATION_ENTER_TREE: {
212 			connect("confirmed", this, "_confirmed");
213 
214 			search_box->set_clear_button_enabled(true);
215 			FALLTHROUGH;
216 		}
217 		case NOTIFICATION_THEME_CHANGED: {
218 			search_box->set_right_icon(get_icon("Search", "EditorIcons"));
219 		} break;
220 		case NOTIFICATION_EXIT_TREE: {
221 			disconnect("confirmed", this, "_confirmed");
222 		} break;
223 	}
224 }
225 
_bind_methods()226 void ScriptEditorQuickOpen::_bind_methods() {
227 
228 	ClassDB::bind_method(D_METHOD("_text_changed"), &ScriptEditorQuickOpen::_text_changed);
229 	ClassDB::bind_method(D_METHOD("_confirmed"), &ScriptEditorQuickOpen::_confirmed);
230 	ClassDB::bind_method(D_METHOD("_sbox_input"), &ScriptEditorQuickOpen::_sbox_input);
231 
232 	ADD_SIGNAL(MethodInfo("goto_line", PropertyInfo(Variant::INT, "line")));
233 }
234 
ScriptEditorQuickOpen()235 ScriptEditorQuickOpen::ScriptEditorQuickOpen() {
236 
237 	VBoxContainer *vbc = memnew(VBoxContainer);
238 	add_child(vbc);
239 	search_box = memnew(LineEdit);
240 	vbc->add_margin_child(TTR("Search:"), search_box);
241 	search_box->connect("text_changed", this, "_text_changed");
242 	search_box->connect("gui_input", this, "_sbox_input");
243 	search_options = memnew(Tree);
244 	vbc->add_margin_child(TTR("Matches:"), search_options, true);
245 	get_ok()->set_text(TTR("Open"));
246 	get_ok()->set_disabled(true);
247 	register_text_enter(search_box);
248 	set_hide_on_ok(false);
249 	search_options->connect("item_activated", this, "_confirmed");
250 	search_options->set_hide_root(true);
251 	search_options->set_hide_folding(true);
252 	search_options->add_constant_override("draw_guides", 1);
253 }
254 
255 /////////////////////////////////
256 
257 ScriptEditor *ScriptEditor::script_editor = NULL;
258 
259 /*** SCRIPT EDITOR ******/
260 
_get_debug_tooltip(const String & p_text,Node * _se)261 String ScriptEditor::_get_debug_tooltip(const String &p_text, Node *_se) {
262 
263 	String val = debugger->get_var_value(p_text);
264 	if (val != String()) {
265 		return p_text + ": " + val;
266 	} else {
267 
268 		return String();
269 	}
270 }
271 
_breaked(bool p_breaked,bool p_can_debug)272 void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) {
273 
274 	if (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) {
275 		return;
276 	}
277 
278 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), !(p_breaked && p_can_debug));
279 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), !(p_breaked && p_can_debug));
280 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), p_breaked);
281 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), !p_breaked);
282 
283 	for (int i = 0; i < tab_container->get_child_count(); i++) {
284 
285 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
286 		if (!se) {
287 			continue;
288 		}
289 
290 		se->set_debugger_active(p_breaked);
291 	}
292 }
293 
_show_debugger(bool p_show)294 void ScriptEditor::_show_debugger(bool p_show) {
295 
296 	//debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), p_show);
297 }
298 
_script_created(Ref<Script> p_script)299 void ScriptEditor::_script_created(Ref<Script> p_script) {
300 	editor->push_item(p_script.operator->());
301 }
302 
_goto_script_line2(int p_line)303 void ScriptEditor::_goto_script_line2(int p_line) {
304 
305 	ScriptEditorBase *current = _get_current_editor();
306 	if (current)
307 		current->goto_line(p_line);
308 }
309 
_goto_script_line(REF p_script,int p_line)310 void ScriptEditor::_goto_script_line(REF p_script, int p_line) {
311 
312 	Ref<Script> script = Object::cast_to<Script>(*p_script);
313 	if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) {
314 		if (edit(p_script, p_line, 0)) {
315 			editor->push_item(p_script.ptr());
316 
317 			ScriptEditorBase *current = _get_current_editor();
318 			if (ScriptTextEditor *script_text_editor = Object::cast_to<ScriptTextEditor>(current)) {
319 				script_text_editor->goto_line_centered(p_line);
320 			} else if (current) {
321 				current->goto_line(p_line, true);
322 			}
323 		}
324 	}
325 }
326 
_set_execution(REF p_script,int p_line)327 void ScriptEditor::_set_execution(REF p_script, int p_line) {
328 	Ref<Script> script = Object::cast_to<Script>(*p_script);
329 	if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) {
330 		for (int i = 0; i < tab_container->get_child_count(); i++) {
331 
332 			ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
333 			if (!se)
334 				continue;
335 
336 			if ((script != NULL && se->get_edited_resource() == p_script) || se->get_edited_resource()->get_path() == script->get_path()) {
337 				se->set_executing_line(p_line);
338 			}
339 		}
340 	}
341 }
342 
_clear_execution(REF p_script)343 void ScriptEditor::_clear_execution(REF p_script) {
344 	Ref<Script> script = Object::cast_to<Script>(*p_script);
345 	if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) {
346 		for (int i = 0; i < tab_container->get_child_count(); i++) {
347 
348 			ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
349 			if (!se)
350 				continue;
351 
352 			if ((script != NULL && se->get_edited_resource() == p_script) || se->get_edited_resource()->get_path() == script->get_path()) {
353 				se->clear_executing_line();
354 			}
355 		}
356 	}
357 }
358 
_get_current_editor() const359 ScriptEditorBase *ScriptEditor::_get_current_editor() const {
360 
361 	int selected = tab_container->get_current_tab();
362 	if (selected < 0 || selected >= tab_container->get_child_count())
363 		return NULL;
364 
365 	return Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
366 }
367 
_update_history_arrows()368 void ScriptEditor::_update_history_arrows() {
369 
370 	script_back->set_disabled(history_pos <= 0);
371 	script_forward->set_disabled(history_pos >= history.size() - 1);
372 }
373 
_save_history()374 void ScriptEditor::_save_history() {
375 
376 	if (history_pos >= 0 && history_pos < history.size() && history[history_pos].control == tab_container->get_current_tab_control()) {
377 
378 		Node *n = tab_container->get_current_tab_control();
379 
380 		if (Object::cast_to<ScriptEditorBase>(n)) {
381 
382 			history.write[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state();
383 		}
384 		if (Object::cast_to<EditorHelp>(n)) {
385 
386 			history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll();
387 		}
388 	}
389 
390 	history.resize(history_pos + 1);
391 	ScriptHistory sh;
392 	sh.control = tab_container->get_current_tab_control();
393 	sh.state = Variant();
394 
395 	history.push_back(sh);
396 	history_pos++;
397 
398 	_update_history_arrows();
399 }
400 
_go_to_tab(int p_idx)401 void ScriptEditor::_go_to_tab(int p_idx) {
402 
403 	ScriptEditorBase *current = _get_current_editor();
404 	if (current) {
405 		if (current->is_unsaved()) {
406 
407 			current->apply_code();
408 		}
409 	}
410 
411 	Control *c = Object::cast_to<Control>(tab_container->get_child(p_idx));
412 	if (!c)
413 		return;
414 
415 	if (history_pos >= 0 && history_pos < history.size() && history[history_pos].control == tab_container->get_current_tab_control()) {
416 
417 		Node *n = tab_container->get_current_tab_control();
418 
419 		if (Object::cast_to<ScriptEditorBase>(n)) {
420 
421 			history.write[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state();
422 		}
423 		if (Object::cast_to<EditorHelp>(n)) {
424 
425 			history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll();
426 		}
427 	}
428 
429 	history.resize(history_pos + 1);
430 	ScriptHistory sh;
431 	sh.control = c;
432 	sh.state = Variant();
433 
434 	history.push_back(sh);
435 	history_pos++;
436 
437 	tab_container->set_current_tab(p_idx);
438 
439 	c = tab_container->get_current_tab_control();
440 
441 	if (Object::cast_to<ScriptEditorBase>(c)) {
442 
443 		script_name_label->set_text(Object::cast_to<ScriptEditorBase>(c)->get_name());
444 		script_icon->set_texture(Object::cast_to<ScriptEditorBase>(c)->get_icon());
445 		if (is_visible_in_tree())
446 			Object::cast_to<ScriptEditorBase>(c)->ensure_focus();
447 
448 		Ref<Script> script = Object::cast_to<ScriptEditorBase>(c)->get_edited_resource();
449 		if (script != NULL) {
450 			notify_script_changed(script);
451 		}
452 
453 		Object::cast_to<ScriptEditorBase>(c)->validate();
454 	}
455 	if (Object::cast_to<EditorHelp>(c)) {
456 
457 		script_name_label->set_text(Object::cast_to<EditorHelp>(c)->get_class());
458 		script_icon->set_texture(get_icon("Help", "EditorIcons"));
459 		if (is_visible_in_tree())
460 			Object::cast_to<EditorHelp>(c)->set_focused();
461 	}
462 
463 	c->set_meta("__editor_pass", ++edit_pass);
464 	_update_history_arrows();
465 	_update_script_colors();
466 	_update_members_overview();
467 	_update_help_overview();
468 	_update_selected_editor_menu();
469 	_update_members_overview_visibility();
470 	_update_help_overview_visibility();
471 }
472 
_add_recent_script(String p_path)473 void ScriptEditor::_add_recent_script(String p_path) {
474 
475 	if (p_path.empty()) {
476 		return;
477 	}
478 
479 	Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array());
480 	if (rc.find(p_path) != -1) {
481 		rc.erase(p_path);
482 	}
483 	rc.push_front(p_path);
484 	if (rc.size() > 10) {
485 		rc.resize(10);
486 	}
487 
488 	EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", rc);
489 	_update_recent_scripts();
490 }
491 
_update_recent_scripts()492 void ScriptEditor::_update_recent_scripts() {
493 
494 	Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array());
495 	recent_scripts->clear();
496 
497 	String path;
498 	for (int i = 0; i < rc.size(); i++) {
499 
500 		path = rc[i];
501 		recent_scripts->add_item(path.replace("res://", ""));
502 	}
503 
504 	recent_scripts->add_separator();
505 	recent_scripts->add_shortcut(ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Files")));
506 
507 	recent_scripts->set_as_minsize();
508 }
509 
_open_recent_script(int p_idx)510 void ScriptEditor::_open_recent_script(int p_idx) {
511 
512 	// clear button
513 	if (p_idx == recent_scripts->get_item_count() - 1) {
514 		EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", Array());
515 		call_deferred("_update_recent_scripts");
516 		return;
517 	}
518 
519 	Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array());
520 	ERR_FAIL_INDEX(p_idx, rc.size());
521 
522 	String path = rc[p_idx];
523 	// if its not on disk its a help file or deleted
524 	if (FileAccess::exists(path)) {
525 		List<String> extensions;
526 		ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
527 
528 		if (extensions.find(path.get_extension())) {
529 			Ref<Script> script = ResourceLoader::load(path);
530 			if (script.is_valid()) {
531 				edit(script, true);
532 				return;
533 			}
534 		}
535 
536 		Error err;
537 		Ref<TextFile> text_file = _load_text_file(path, &err);
538 		if (text_file.is_valid()) {
539 			edit(text_file, true);
540 			return;
541 		}
542 		// if it's a path then it's most likely a deleted file not help
543 	} else if (path.find("::") != -1) {
544 		// built-in script
545 		String res_path = path.get_slice("::", 0);
546 		if (ResourceLoader::get_resource_type(res_path) == "PackedScene") {
547 			if (!EditorNode::get_singleton()->is_scene_open(res_path)) {
548 				EditorNode::get_singleton()->load_scene(res_path);
549 			}
550 		} else {
551 			EditorNode::get_singleton()->load_resource(res_path);
552 		}
553 		Ref<Script> script = ResourceLoader::load(path);
554 		if (script.is_valid()) {
555 			edit(script, true);
556 			return;
557 		}
558 	} else if (!path.is_resource_file()) {
559 		_help_class_open(path);
560 		return;
561 	}
562 
563 	rc.remove(p_idx);
564 	EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", rc);
565 	_update_recent_scripts();
566 	_show_error_dialog(path);
567 }
568 
_show_error_dialog(String p_path)569 void ScriptEditor::_show_error_dialog(String p_path) {
570 
571 	error_dialog->set_text(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_path));
572 	error_dialog->popup_centered_minsize();
573 }
574 
_close_tab(int p_idx,bool p_save,bool p_history_back)575 void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
576 
577 	int selected = p_idx;
578 	if (selected < 0 || selected >= tab_container->get_child_count())
579 		return;
580 
581 	Node *tselected = tab_container->get_child(selected);
582 
583 	ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
584 	if (current) {
585 		Ref<Script> script = current->get_edited_resource();
586 		if (p_save) {
587 			// Do not try to save internal scripts
588 			if (!script.is_valid() || !(script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1)) {
589 				_menu_option(FILE_SAVE);
590 			}
591 		}
592 
593 		if (script != NULL) {
594 			previous_scripts.push_back(script->get_path());
595 			notify_script_close(script);
596 		}
597 	}
598 
599 	// roll back to previous tab
600 	if (p_history_back) {
601 		_history_back();
602 	}
603 
604 	//remove from history
605 	history.resize(history_pos + 1);
606 
607 	for (int i = 0; i < history.size(); i++) {
608 		if (history[i].control == tselected) {
609 			history.remove(i);
610 			i--;
611 			history_pos--;
612 		}
613 	}
614 
615 	if (history_pos >= history.size()) {
616 		history_pos = history.size() - 1;
617 	}
618 
619 	int idx = tab_container->get_current_tab();
620 	if (current) {
621 		current->clear_edit_menu();
622 	}
623 	memdelete(tselected);
624 	if (idx >= tab_container->get_child_count())
625 		idx = tab_container->get_child_count() - 1;
626 	if (idx >= 0) {
627 
628 		if (history_pos >= 0) {
629 			idx = history[history_pos].control->get_index();
630 		}
631 		tab_container->set_current_tab(idx);
632 	} else {
633 		_update_selected_editor_menu();
634 	}
635 
636 	_update_history_arrows();
637 
638 	_update_script_names();
639 	_update_members_overview_visibility();
640 	_update_help_overview_visibility();
641 	_save_layout();
642 }
643 
_close_current_tab()644 void ScriptEditor::_close_current_tab() {
645 
646 	_close_tab(tab_container->get_current_tab());
647 }
648 
_close_discard_current_tab(const String & p_str)649 void ScriptEditor::_close_discard_current_tab(const String &p_str) {
650 	_close_tab(tab_container->get_current_tab(), false);
651 	erase_tab_confirm->hide();
652 }
653 
_close_docs_tab()654 void ScriptEditor::_close_docs_tab() {
655 
656 	int child_count = tab_container->get_child_count();
657 	for (int i = child_count - 1; i >= 0; i--) {
658 
659 		EditorHelp *se = Object::cast_to<EditorHelp>(tab_container->get_child(i));
660 
661 		if (se) {
662 			_close_tab(i, true, false);
663 		}
664 	}
665 }
666 
_copy_script_path()667 void ScriptEditor::_copy_script_path() {
668 	ScriptEditorBase *se = _get_current_editor();
669 	RES script = se->get_edited_resource();
670 	OS::get_singleton()->set_clipboard(script->get_path());
671 }
672 
_close_other_tabs()673 void ScriptEditor::_close_other_tabs() {
674 
675 	int child_count = tab_container->get_child_count();
676 	int current_idx = tab_container->get_current_tab();
677 	for (int i = child_count - 1; i >= 0; i--) {
678 
679 		if (i == current_idx) {
680 			continue;
681 		}
682 
683 		tab_container->set_current_tab(i);
684 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
685 
686 		if (se) {
687 
688 			// Maybe there are unsaved changes
689 			if (se->is_unsaved()) {
690 				_ask_close_current_unsaved_tab(se);
691 				continue;
692 			}
693 		}
694 
695 		_close_current_tab();
696 	}
697 }
698 
_close_all_tabs()699 void ScriptEditor::_close_all_tabs() {
700 
701 	int child_count = tab_container->get_child_count();
702 	for (int i = child_count - 1; i >= 0; i--) {
703 
704 		tab_container->set_current_tab(i);
705 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
706 
707 		if (se) {
708 
709 			// Maybe there are unsaved changes
710 			if (se->is_unsaved()) {
711 				_ask_close_current_unsaved_tab(se);
712 				continue;
713 			}
714 		}
715 
716 		_close_current_tab();
717 	}
718 }
719 
_ask_close_current_unsaved_tab(ScriptEditorBase * current)720 void ScriptEditor::_ask_close_current_unsaved_tab(ScriptEditorBase *current) {
721 	erase_tab_confirm->set_text(TTR("Close and save changes?") + "\n\"" + current->get_name() + "\"");
722 	erase_tab_confirm->popup_centered_minsize();
723 }
724 
_resave_scripts(const String & p_str)725 void ScriptEditor::_resave_scripts(const String &p_str) {
726 
727 	apply_scripts();
728 
729 	for (int i = 0; i < tab_container->get_child_count(); i++) {
730 
731 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
732 		if (!se)
733 			continue;
734 
735 		RES script = se->get_edited_resource();
736 
737 		if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1)
738 			continue; //internal script, who cares
739 
740 		if (trim_trailing_whitespace_on_save) {
741 			se->trim_trailing_whitespace();
742 		}
743 
744 		se->insert_final_newline();
745 
746 		if (convert_indent_on_save) {
747 			if (use_space_indentation) {
748 				se->convert_indent_to_spaces();
749 			} else {
750 				se->convert_indent_to_tabs();
751 			}
752 		}
753 
754 		Ref<TextFile> text_file = script;
755 		if (text_file != NULL) {
756 			se->apply_code();
757 			_save_text_file(text_file, text_file->get_path());
758 			break;
759 		} else {
760 			editor->save_resource(script);
761 		}
762 		se->tag_saved_version();
763 	}
764 
765 	disk_changed->hide();
766 }
767 
_reload_scripts()768 void ScriptEditor::_reload_scripts() {
769 
770 	for (int i = 0; i < tab_container->get_child_count(); i++) {
771 
772 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
773 		if (!se) {
774 
775 			continue;
776 		}
777 
778 		RES edited_res = se->get_edited_resource();
779 
780 		if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1) {
781 
782 			continue; //internal script, who cares
783 		}
784 
785 		uint64_t last_date = edited_res->get_last_modified_time();
786 		uint64_t date = FileAccess::get_modified_time(edited_res->get_path());
787 
788 		if (last_date == date) {
789 			continue;
790 		}
791 
792 		Ref<Script> script = edited_res;
793 		if (script != NULL) {
794 			Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), true);
795 			ERR_CONTINUE(!rel_script.is_valid());
796 			script->set_source_code(rel_script->get_source_code());
797 			script->set_last_modified_time(rel_script->get_last_modified_time());
798 			script->reload();
799 		}
800 
801 		Ref<TextFile> text_file = edited_res;
802 		if (text_file != NULL) {
803 			Error err;
804 			Ref<TextFile> rel_text_file = _load_text_file(text_file->get_path(), &err);
805 			ERR_CONTINUE(!rel_text_file.is_valid());
806 			text_file->set_text(rel_text_file->get_text());
807 			text_file->set_last_modified_time(rel_text_file->get_last_modified_time());
808 		}
809 		se->reload_text();
810 	}
811 
812 	disk_changed->hide();
813 	_update_script_names();
814 }
815 
_res_saved_callback(const Ref<Resource> & p_res)816 void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
817 
818 	for (int i = 0; i < tab_container->get_child_count(); i++) {
819 
820 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
821 		if (!se) {
822 
823 			continue;
824 		}
825 
826 		RES script = se->get_edited_resource();
827 
828 		if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) {
829 			continue; //internal script, who cares
830 		}
831 
832 		if (script == p_res) {
833 
834 			se->tag_saved_version();
835 		}
836 	}
837 
838 	_update_script_names();
839 
840 	if (!pending_auto_reload && auto_reload_running_scripts) {
841 		call_deferred("_live_auto_reload_running_scripts");
842 		pending_auto_reload = true;
843 	}
844 }
845 
_live_auto_reload_running_scripts()846 void ScriptEditor::_live_auto_reload_running_scripts() {
847 	pending_auto_reload = false;
848 	debugger->reload_scripts();
849 }
850 
_test_script_times_on_disk(RES p_for_script)851 bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) {
852 
853 	disk_changed_list->clear();
854 	TreeItem *r = disk_changed_list->create_item();
855 	disk_changed_list->set_hide_root(true);
856 
857 	bool need_ask = false;
858 	bool need_reload = false;
859 	bool use_autoreload = bool(EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change", false));
860 
861 	for (int i = 0; i < tab_container->get_child_count(); i++) {
862 
863 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
864 		if (se) {
865 
866 			RES edited_res = se->get_edited_resource();
867 			if (p_for_script.is_valid() && edited_res.is_valid() && p_for_script != edited_res)
868 				continue;
869 
870 			if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1)
871 				continue; //internal script, who cares
872 
873 			uint64_t last_date = edited_res->get_last_modified_time();
874 			uint64_t date = FileAccess::get_modified_time(edited_res->get_path());
875 
876 			if (last_date != date) {
877 
878 				TreeItem *ti = disk_changed_list->create_item(r);
879 				ti->set_text(0, edited_res->get_path().get_file());
880 
881 				if (!use_autoreload || se->is_unsaved()) {
882 					need_ask = true;
883 				}
884 				need_reload = true;
885 			}
886 		}
887 	}
888 
889 	if (need_reload) {
890 		if (!need_ask) {
891 			script_editor->_reload_scripts();
892 			need_reload = false;
893 		} else {
894 			disk_changed->call_deferred("popup_centered_ratio", 0.5);
895 		}
896 	}
897 
898 	return need_reload;
899 }
900 
_file_dialog_action(String p_file)901 void ScriptEditor::_file_dialog_action(String p_file) {
902 
903 	switch (file_dialog_option) {
904 		case FILE_NEW_TEXTFILE: {
905 			Error err;
906 			FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err);
907 			if (err) {
908 				editor->show_warning(TTR("Error writing TextFile:") + "\n" + p_file, TTR("Error!"));
909 				break;
910 			}
911 			file->close();
912 			memdelete(file);
913 			FALLTHROUGH;
914 		}
915 		case FILE_OPEN: {
916 
917 			List<String> extensions;
918 			ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
919 			if (extensions.find(p_file.get_extension())) {
920 				Ref<Script> scr = ResourceLoader::load(p_file);
921 				if (!scr.is_valid()) {
922 					editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
923 					file_dialog_option = -1;
924 					return;
925 				}
926 
927 				edit(scr);
928 				file_dialog_option = -1;
929 				return;
930 			}
931 
932 			Error error;
933 			Ref<TextFile> text_file = _load_text_file(p_file, &error);
934 			if (error != OK) {
935 				editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
936 			}
937 
938 			if (text_file.is_valid()) {
939 				edit(text_file);
940 				file_dialog_option = -1;
941 				return;
942 			}
943 		} break;
944 		case FILE_SAVE_AS: {
945 			ScriptEditorBase *current = _get_current_editor();
946 
947 			String path = ProjectSettings::get_singleton()->localize_path(p_file);
948 			Error err = _save_text_file(current->get_edited_resource(), path);
949 
950 			if (err != OK) {
951 				editor->show_accept(TTR("Error saving file!"), TTR("OK"));
952 				return;
953 			}
954 
955 			((Resource *)current->get_edited_resource().ptr())->set_path(path);
956 			_update_script_names();
957 		} break;
958 		case THEME_SAVE_AS: {
959 			if (!EditorSettings::get_singleton()->save_text_editor_theme_as(p_file)) {
960 				editor->show_warning(TTR("Error while saving theme."), TTR("Error Saving"));
961 			}
962 		} break;
963 		case THEME_IMPORT: {
964 			if (!EditorSettings::get_singleton()->import_text_editor_theme(p_file)) {
965 				editor->show_warning(TTR("Error importing theme."), TTR("Error Importing"));
966 			}
967 		} break;
968 	}
969 	file_dialog_option = -1;
970 }
971 
_get_current_script()972 Ref<Script> ScriptEditor::_get_current_script() {
973 
974 	ScriptEditorBase *current = _get_current_editor();
975 
976 	if (current) {
977 		Ref<Script> script = current->get_edited_resource();
978 		return script != NULL ? script : NULL;
979 	} else {
980 		return NULL;
981 	}
982 }
983 
_get_open_scripts() const984 Array ScriptEditor::_get_open_scripts() const {
985 
986 	Array ret;
987 	Vector<Ref<Script> > scripts = get_open_scripts();
988 	int scrits_amount = scripts.size();
989 	for (int idx_script = 0; idx_script < scrits_amount; idx_script++) {
990 		ret.push_back(scripts[idx_script]);
991 	}
992 	return ret;
993 }
994 
toggle_scripts_panel()995 bool ScriptEditor::toggle_scripts_panel() {
996 	list_split->set_visible(!list_split->is_visible());
997 	return list_split->is_visible();
998 }
999 
is_scripts_panel_toggled()1000 bool ScriptEditor::is_scripts_panel_toggled() {
1001 	return list_split->is_visible();
1002 }
1003 
_menu_option(int p_option)1004 void ScriptEditor::_menu_option(int p_option) {
1005 
1006 	ScriptEditorBase *current = _get_current_editor();
1007 	switch (p_option) {
1008 		case FILE_NEW: {
1009 			script_create_dialog->config("Node", "new_script", false, false);
1010 			script_create_dialog->popup_centered();
1011 		} break;
1012 		case FILE_NEW_TEXTFILE: {
1013 			file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
1014 			file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1015 			file_dialog_option = FILE_NEW_TEXTFILE;
1016 
1017 			file_dialog->clear_filters();
1018 			file_dialog->popup_centered_ratio();
1019 			file_dialog->set_title(TTR("New Text File..."));
1020 		} break;
1021 		case FILE_OPEN: {
1022 			file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
1023 			file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1024 			file_dialog_option = FILE_OPEN;
1025 
1026 			List<String> extensions;
1027 			ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
1028 			file_dialog->clear_filters();
1029 			for (int i = 0; i < extensions.size(); i++) {
1030 				file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
1031 			}
1032 
1033 			file_dialog->popup_centered_ratio();
1034 			file_dialog->set_title(TTR("Open File"));
1035 			return;
1036 		} break;
1037 		case FILE_REOPEN_CLOSED: {
1038 
1039 			if (previous_scripts.empty())
1040 				return;
1041 
1042 			String path = previous_scripts.back()->get();
1043 			previous_scripts.pop_back();
1044 
1045 			List<String> extensions;
1046 			ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
1047 			bool built_in = !path.is_resource_file();
1048 
1049 			if (extensions.find(path.get_extension()) || built_in) {
1050 				if (built_in) {
1051 					String res_path = path.get_slice("::", 0);
1052 					if (ResourceLoader::get_resource_type(res_path) == "PackedScene") {
1053 						if (!EditorNode::get_singleton()->is_scene_open(res_path)) {
1054 							EditorNode::get_singleton()->load_scene(res_path);
1055 							script_editor->call_deferred("_menu_option", p_option);
1056 							previous_scripts.push_back(path); //repeat the operation
1057 							return;
1058 						}
1059 					} else {
1060 						EditorNode::get_singleton()->load_resource(res_path);
1061 					}
1062 				}
1063 
1064 				Ref<Script> scr = ResourceLoader::load(path);
1065 				if (!scr.is_valid()) {
1066 					editor->show_warning(TTR("Could not load file at:") + "\n\n" + path, TTR("Error!"));
1067 					file_dialog_option = -1;
1068 					return;
1069 				}
1070 
1071 				edit(scr);
1072 				file_dialog_option = -1;
1073 				return;
1074 			} else {
1075 				Error error;
1076 				Ref<TextFile> text_file = _load_text_file(path, &error);
1077 				if (error != OK)
1078 					editor->show_warning(TTR("Could not load file at:") + "\n\n" + path, TTR("Error!"));
1079 
1080 				if (text_file.is_valid()) {
1081 					edit(text_file);
1082 					file_dialog_option = -1;
1083 					return;
1084 				}
1085 			}
1086 		} break;
1087 		case FILE_SAVE_ALL: {
1088 
1089 			if (_test_script_times_on_disk())
1090 				return;
1091 
1092 			save_all_scripts();
1093 		} break;
1094 		case SEARCH_IN_FILES: {
1095 
1096 			_on_find_in_files_requested("");
1097 		} break;
1098 		case SEARCH_HELP: {
1099 
1100 			help_search_dialog->popup_dialog();
1101 		} break;
1102 		case SEARCH_WEBSITE: {
1103 
1104 			OS::get_singleton()->shell_open("https://docs.godotengine.org/");
1105 		} break;
1106 		case WINDOW_NEXT: {
1107 
1108 			_history_forward();
1109 		} break;
1110 		case WINDOW_PREV: {
1111 
1112 			_history_back();
1113 		} break;
1114 		case WINDOW_SORT: {
1115 			_sort_list_on_update = true;
1116 			_update_script_names();
1117 		} break;
1118 		case DEBUG_SHOW: {
1119 			if (debugger) {
1120 				bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW));
1121 				debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible);
1122 				if (visible)
1123 					debugger->hide();
1124 				else
1125 					debugger->show();
1126 			}
1127 		} break;
1128 		case DEBUG_SHOW_KEEP_OPEN: {
1129 			bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN));
1130 			if (debugger)
1131 				debugger->set_hide_on_stop(visible);
1132 			debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible);
1133 		} break;
1134 		case DEBUG_WITH_EXTERNAL_EDITOR: {
1135 			bool debug_with_external_editor = !debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR));
1136 			debugger->set_debug_with_external_editor(debug_with_external_editor);
1137 			debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), debug_with_external_editor);
1138 		} break;
1139 		case TOGGLE_SCRIPTS_PANEL: {
1140 			if (current) {
1141 				ScriptTextEditor *editor = Object::cast_to<ScriptTextEditor>(current);
1142 				toggle_scripts_panel();
1143 				if (editor) {
1144 					editor->update_toggle_scripts_button();
1145 				}
1146 			} else {
1147 				toggle_scripts_panel();
1148 			}
1149 		}
1150 	}
1151 
1152 	if (current) {
1153 
1154 		switch (p_option) {
1155 			case FILE_SAVE: {
1156 
1157 				if (_test_script_times_on_disk())
1158 					return;
1159 
1160 				if (trim_trailing_whitespace_on_save)
1161 					current->trim_trailing_whitespace();
1162 
1163 				current->insert_final_newline();
1164 
1165 				if (convert_indent_on_save) {
1166 					if (use_space_indentation) {
1167 						current->convert_indent_to_spaces();
1168 					} else {
1169 						current->convert_indent_to_tabs();
1170 					}
1171 				}
1172 
1173 				Ref<TextFile> text_file = current->get_edited_resource();
1174 				if (text_file != NULL) {
1175 					current->apply_code();
1176 					_save_text_file(text_file, text_file->get_path());
1177 					break;
1178 				}
1179 				editor->save_resource(current->get_edited_resource());
1180 
1181 			} break;
1182 			case FILE_SAVE_AS: {
1183 
1184 				if (trim_trailing_whitespace_on_save)
1185 					current->trim_trailing_whitespace();
1186 
1187 				current->insert_final_newline();
1188 
1189 				if (convert_indent_on_save) {
1190 					if (use_space_indentation) {
1191 						current->convert_indent_to_spaces();
1192 					} else {
1193 						current->convert_indent_to_tabs();
1194 					}
1195 				}
1196 
1197 				Ref<TextFile> text_file = current->get_edited_resource();
1198 				if (text_file != NULL) {
1199 					file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
1200 					file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1201 					file_dialog_option = FILE_SAVE_AS;
1202 
1203 					List<String> extensions;
1204 					ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
1205 					file_dialog->clear_filters();
1206 					file_dialog->set_current_dir(text_file->get_path().get_base_dir());
1207 					file_dialog->set_current_file(text_file->get_path().get_file());
1208 					file_dialog->popup_centered_ratio();
1209 					file_dialog->set_title(TTR("Save File As..."));
1210 					break;
1211 				}
1212 
1213 				editor->push_item(Object::cast_to<Object>(current->get_edited_resource().ptr()));
1214 				editor->save_resource_as(current->get_edited_resource());
1215 
1216 			} break;
1217 
1218 			case FILE_TOOL_RELOAD:
1219 			case FILE_TOOL_RELOAD_SOFT: {
1220 
1221 				current->reload(p_option == FILE_TOOL_RELOAD_SOFT);
1222 
1223 			} break;
1224 			case FILE_RUN: {
1225 
1226 				Ref<Script> scr = current->get_edited_resource();
1227 				if (scr == NULL || scr.is_null()) {
1228 					EditorNode::get_singleton()->show_warning(TTR("Can't obtain the script for running."));
1229 					break;
1230 				}
1231 
1232 				current->apply_code();
1233 				Error err = scr->reload(false); //hard reload script before running always
1234 
1235 				if (err != OK) {
1236 					EditorNode::get_singleton()->show_warning(TTR("Script failed reloading, check console for errors."));
1237 					return;
1238 				}
1239 				if (!scr->is_tool()) {
1240 
1241 					EditorNode::get_singleton()->show_warning(TTR("Script is not in tool mode, will not be able to run."));
1242 					return;
1243 				}
1244 
1245 				if (!ClassDB::is_parent_class(scr->get_instance_base_type(), "EditorScript")) {
1246 
1247 					EditorNode::get_singleton()->show_warning(TTR("To run this script, it must inherit EditorScript and be set to tool mode."));
1248 					return;
1249 				}
1250 
1251 				Ref<EditorScript> es = memnew(EditorScript);
1252 				es->set_script(scr.get_ref_ptr());
1253 				es->set_editor(EditorNode::get_singleton());
1254 
1255 				es->_run();
1256 
1257 				EditorNode::get_undo_redo()->clear_history();
1258 			} break;
1259 			case FILE_CLOSE: {
1260 				if (current->is_unsaved()) {
1261 					_ask_close_current_unsaved_tab(current);
1262 				} else {
1263 					_close_current_tab();
1264 				}
1265 			} break;
1266 			case FILE_COPY_PATH: {
1267 				_copy_script_path();
1268 			} break;
1269 			case SHOW_IN_FILE_SYSTEM: {
1270 				const RES script = current->get_edited_resource();
1271 				const String path = script->get_path();
1272 				if (!path.empty()) {
1273 					FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
1274 					file_system_dock->navigate_to_path(path);
1275 					// Ensure that the FileSystem dock is visible.
1276 					TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control();
1277 					tab_container->set_current_tab(file_system_dock->get_position_in_parent());
1278 				}
1279 			} break;
1280 			case CLOSE_DOCS: {
1281 				_close_docs_tab();
1282 			} break;
1283 			case CLOSE_OTHER_TABS: {
1284 				_close_other_tabs();
1285 			} break;
1286 			case CLOSE_ALL: {
1287 				_close_all_tabs();
1288 			} break;
1289 			case DEBUG_NEXT: {
1290 
1291 				if (debugger)
1292 					debugger->debug_next();
1293 			} break;
1294 			case DEBUG_STEP: {
1295 
1296 				if (debugger)
1297 					debugger->debug_step();
1298 
1299 			} break;
1300 			case DEBUG_BREAK: {
1301 
1302 				if (debugger)
1303 					debugger->debug_break();
1304 
1305 			} break;
1306 			case DEBUG_CONTINUE: {
1307 
1308 				if (debugger)
1309 					debugger->debug_continue();
1310 
1311 			} break;
1312 			case WINDOW_MOVE_UP: {
1313 
1314 				if (tab_container->get_current_tab() > 0) {
1315 					tab_container->move_child(current, tab_container->get_current_tab() - 1);
1316 					tab_container->set_current_tab(tab_container->get_current_tab() - 1);
1317 					_update_script_names();
1318 				}
1319 			} break;
1320 			case WINDOW_MOVE_DOWN: {
1321 
1322 				if (tab_container->get_current_tab() < tab_container->get_child_count() - 1) {
1323 					tab_container->move_child(current, tab_container->get_current_tab() + 1);
1324 					tab_container->set_current_tab(tab_container->get_current_tab() + 1);
1325 					_update_script_names();
1326 				}
1327 			} break;
1328 			default: {
1329 
1330 				if (p_option >= WINDOW_SELECT_BASE) {
1331 
1332 					tab_container->set_current_tab(p_option - WINDOW_SELECT_BASE);
1333 					_update_script_names();
1334 				}
1335 			}
1336 		}
1337 	} else {
1338 
1339 		EditorHelp *help = Object::cast_to<EditorHelp>(tab_container->get_current_tab_control());
1340 		if (help) {
1341 
1342 			switch (p_option) {
1343 				case HELP_SEARCH_FIND: {
1344 					help->popup_search();
1345 				} break;
1346 				case HELP_SEARCH_FIND_NEXT: {
1347 					help->search_again();
1348 				} break;
1349 				case HELP_SEARCH_FIND_PREVIOUS: {
1350 					help->search_again(true);
1351 				} break;
1352 				case FILE_CLOSE: {
1353 					_close_current_tab();
1354 				} break;
1355 				case CLOSE_DOCS: {
1356 					_close_docs_tab();
1357 				} break;
1358 				case CLOSE_OTHER_TABS: {
1359 					_close_other_tabs();
1360 				} break;
1361 				case CLOSE_ALL: {
1362 					_close_all_tabs();
1363 				} break;
1364 				case WINDOW_MOVE_UP: {
1365 
1366 					if (tab_container->get_current_tab() > 0) {
1367 						tab_container->move_child(help, tab_container->get_current_tab() - 1);
1368 						tab_container->set_current_tab(tab_container->get_current_tab() - 1);
1369 						_update_script_names();
1370 					}
1371 				} break;
1372 				case WINDOW_MOVE_DOWN: {
1373 
1374 					if (tab_container->get_current_tab() < tab_container->get_child_count() - 1) {
1375 						tab_container->move_child(help, tab_container->get_current_tab() + 1);
1376 						tab_container->set_current_tab(tab_container->get_current_tab() + 1);
1377 						_update_script_names();
1378 					}
1379 				} break;
1380 			}
1381 		}
1382 	}
1383 }
1384 
_theme_option(int p_option)1385 void ScriptEditor::_theme_option(int p_option) {
1386 	switch (p_option) {
1387 		case THEME_IMPORT: {
1388 			file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
1389 			file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1390 			file_dialog_option = THEME_IMPORT;
1391 			file_dialog->clear_filters();
1392 			file_dialog->add_filter("*.tet");
1393 			file_dialog->popup_centered_ratio();
1394 			file_dialog->set_title(TTR("Import Theme"));
1395 		} break;
1396 		case THEME_RELOAD: {
1397 			EditorSettings::get_singleton()->load_text_editor_theme();
1398 		} break;
1399 		case THEME_SAVE: {
1400 			if (EditorSettings::get_singleton()->is_default_text_editor_theme()) {
1401 				ScriptEditor::_show_save_theme_as_dialog();
1402 			} else if (!EditorSettings::get_singleton()->save_text_editor_theme()) {
1403 				editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
1404 			}
1405 		} break;
1406 		case THEME_SAVE_AS: {
1407 			ScriptEditor::_show_save_theme_as_dialog();
1408 		} break;
1409 	}
1410 }
1411 
_show_save_theme_as_dialog()1412 void ScriptEditor::_show_save_theme_as_dialog() {
1413 	file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
1414 	file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1415 	file_dialog_option = THEME_SAVE_AS;
1416 	file_dialog->clear_filters();
1417 	file_dialog->add_filter("*.tet");
1418 	file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme")));
1419 	file_dialog->popup_centered_ratio();
1420 	file_dialog->set_title(TTR("Save Theme As..."));
1421 }
1422 
_tab_changed(int p_which)1423 void ScriptEditor::_tab_changed(int p_which) {
1424 
1425 	ensure_select_current();
1426 }
1427 
_notification(int p_what)1428 void ScriptEditor::_notification(int p_what) {
1429 
1430 	switch (p_what) {
1431 
1432 		case NOTIFICATION_ENTER_TREE: {
1433 
1434 			editor->connect("play_pressed", this, "_editor_play");
1435 			editor->connect("pause_pressed", this, "_editor_pause");
1436 			editor->connect("stop_pressed", this, "_editor_stop");
1437 			editor->connect("script_add_function_request", this, "_add_callback");
1438 			editor->connect("resource_saved", this, "_res_saved_callback");
1439 			script_list->connect("item_selected", this, "_script_selected");
1440 
1441 			members_overview->connect("item_selected", this, "_members_overview_selected");
1442 			help_overview->connect("item_selected", this, "_help_overview_selected");
1443 			script_split->connect("dragged", this, "_script_split_dragged");
1444 
1445 			EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed");
1446 			FALLTHROUGH;
1447 		}
1448 		case NOTIFICATION_THEME_CHANGED: {
1449 
1450 			help_search->set_icon(get_icon("HelpSearch", "EditorIcons"));
1451 			site_search->set_icon(get_icon("Instance", "EditorIcons"));
1452 
1453 			script_forward->set_icon(get_icon("Forward", "EditorIcons"));
1454 			script_back->set_icon(get_icon("Back", "EditorIcons"));
1455 
1456 			members_overview_alphabeta_sort_button->set_icon(get_icon("Sort", "EditorIcons"));
1457 
1458 			filter_scripts->set_right_icon(get_icon("Search", "EditorIcons"));
1459 			filter_methods->set_right_icon(get_icon("Search", "EditorIcons"));
1460 
1461 			filename->add_style_override("normal", editor->get_gui_base()->get_stylebox("normal", "LineEdit"));
1462 
1463 			recent_scripts->set_as_minsize();
1464 		} break;
1465 
1466 		case NOTIFICATION_READY: {
1467 
1468 			get_tree()->connect("tree_changed", this, "_tree_changed");
1469 			editor->get_inspector_dock()->connect("request_help", this, "_request_help");
1470 			editor->connect("request_help_search", this, "_help_search");
1471 		} break;
1472 
1473 		case NOTIFICATION_EXIT_TREE: {
1474 
1475 			editor->disconnect("play_pressed", this, "_editor_play");
1476 			editor->disconnect("pause_pressed", this, "_editor_pause");
1477 			editor->disconnect("stop_pressed", this, "_editor_stop");
1478 		} break;
1479 
1480 		case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
1481 
1482 			_test_script_times_on_disk();
1483 			_update_modified_scripts_for_external_editor();
1484 		} break;
1485 
1486 		case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
1487 
1488 			if (is_visible()) {
1489 				find_in_files_button->show();
1490 			} else {
1491 				if (find_in_files->is_visible_in_tree()) {
1492 					editor->hide_bottom_panel();
1493 				}
1494 				find_in_files_button->hide();
1495 			}
1496 
1497 		} break;
1498 
1499 		default:
1500 			break;
1501 	}
1502 }
1503 
can_take_away_focus() const1504 bool ScriptEditor::can_take_away_focus() const {
1505 
1506 	ScriptEditorBase *current = _get_current_editor();
1507 	if (current)
1508 		return current->can_lose_focus_on_node_selection();
1509 	else
1510 		return true;
1511 }
1512 
close_builtin_scripts_from_scene(const String & p_scene)1513 void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
1514 
1515 	for (int i = 0; i < tab_container->get_child_count(); i++) {
1516 
1517 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
1518 
1519 		if (se) {
1520 
1521 			Ref<Script> script = se->get_edited_resource();
1522 			if (script == NULL || !script.is_valid())
1523 				continue;
1524 
1525 			if (script->get_path().find("::") != -1 && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed
1526 				_close_tab(i);
1527 				i--;
1528 			}
1529 		}
1530 	}
1531 }
1532 
edited_scene_changed()1533 void ScriptEditor::edited_scene_changed() {
1534 
1535 	_update_modified_scripts_for_external_editor();
1536 }
1537 
notify_script_close(const Ref<Script> & p_script)1538 void ScriptEditor::notify_script_close(const Ref<Script> &p_script) {
1539 	emit_signal("script_close", p_script);
1540 }
1541 
notify_script_changed(const Ref<Script> & p_script)1542 void ScriptEditor::notify_script_changed(const Ref<Script> &p_script) {
1543 	emit_signal("editor_script_changed", p_script);
1544 }
1545 
get_breakpoints(List<String> * p_breakpoints)1546 void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
1547 
1548 	for (int i = 0; i < tab_container->get_child_count(); i++) {
1549 
1550 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
1551 		if (!se)
1552 			continue;
1553 
1554 		Ref<Script> script = se->get_edited_resource();
1555 		if (script == NULL) {
1556 			continue;
1557 		}
1558 
1559 		List<int> bpoints;
1560 		se->get_breakpoints(&bpoints);
1561 		String base = script->get_path();
1562 		if (base.begins_with("local://") || base == "") {
1563 			continue;
1564 		}
1565 
1566 		for (List<int>::Element *E = bpoints.front(); E; E = E->next()) {
1567 
1568 			p_breakpoints->push_back(base + ":" + itos(E->get() + 1));
1569 		}
1570 	}
1571 }
1572 
ensure_focus_current()1573 void ScriptEditor::ensure_focus_current() {
1574 
1575 	if (!is_inside_tree())
1576 		return;
1577 
1578 	ScriptEditorBase *current = _get_current_editor();
1579 	if (current)
1580 		current->ensure_focus();
1581 }
1582 
_members_overview_selected(int p_idx)1583 void ScriptEditor::_members_overview_selected(int p_idx) {
1584 	ScriptEditorBase *se = _get_current_editor();
1585 	if (!se) {
1586 		return;
1587 	}
1588 	// Go to the member's line and reset the cursor column. We can't change scroll_position
1589 	// directly until we have gone to the line first, since code might be folded.
1590 	se->goto_line(members_overview->get_item_metadata(p_idx));
1591 	Dictionary state = se->get_edit_state();
1592 	state["column"] = 0;
1593 	state["scroll_position"] = members_overview->get_item_metadata(p_idx);
1594 	se->set_edit_state(state);
1595 }
1596 
_help_overview_selected(int p_idx)1597 void ScriptEditor::_help_overview_selected(int p_idx) {
1598 	Node *current = tab_container->get_child(tab_container->get_current_tab());
1599 	EditorHelp *se = Object::cast_to<EditorHelp>(current);
1600 	if (!se) {
1601 		return;
1602 	}
1603 	se->scroll_to_section(help_overview->get_item_metadata(p_idx));
1604 }
1605 
_script_selected(int p_idx)1606 void ScriptEditor::_script_selected(int p_idx) {
1607 
1608 	grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(1); //amazing hack, simply amazing
1609 
1610 	_go_to_tab(script_list->get_item_metadata(p_idx));
1611 	grab_focus_block = false;
1612 }
1613 
ensure_select_current()1614 void ScriptEditor::ensure_select_current() {
1615 
1616 	if (tab_container->get_child_count() && tab_container->get_current_tab() >= 0) {
1617 
1618 		ScriptEditorBase *se = _get_current_editor();
1619 		if (se) {
1620 
1621 			if (!grab_focus_block && is_visible_in_tree())
1622 				se->ensure_focus();
1623 		}
1624 	}
1625 
1626 	_update_selected_editor_menu();
1627 }
1628 
_find_scripts(Node * p_base,Node * p_current,Set<Ref<Script>> & used)1629 void ScriptEditor::_find_scripts(Node *p_base, Node *p_current, Set<Ref<Script> > &used) {
1630 	if (p_current != p_base && p_current->get_owner() != p_base)
1631 		return;
1632 
1633 	if (p_current->get_script_instance()) {
1634 		Ref<Script> scr = p_current->get_script();
1635 		if (scr.is_valid())
1636 			used.insert(scr);
1637 	}
1638 
1639 	for (int i = 0; i < p_current->get_child_count(); i++) {
1640 		_find_scripts(p_base, p_current->get_child(i), used);
1641 	}
1642 }
1643 
1644 struct _ScriptEditorItemData {
1645 
1646 	String name;
1647 	String sort_key;
1648 	Ref<Texture> icon;
1649 	int index;
1650 	String tooltip;
1651 	bool used;
1652 	int category;
1653 	Node *ref;
1654 
operator <_ScriptEditorItemData1655 	bool operator<(const _ScriptEditorItemData &id) const {
1656 
1657 		if (category == id.category) {
1658 			if (sort_key == id.sort_key) {
1659 				return index < id.index;
1660 			} else {
1661 				return sort_key < id.sort_key;
1662 			}
1663 		} else {
1664 			return category < id.category;
1665 		}
1666 	}
1667 };
1668 
_update_members_overview_visibility()1669 void ScriptEditor::_update_members_overview_visibility() {
1670 
1671 	ScriptEditorBase *se = _get_current_editor();
1672 	if (!se) {
1673 		members_overview_alphabeta_sort_button->set_visible(false);
1674 		members_overview->set_visible(false);
1675 		overview_vbox->set_visible(false);
1676 		return;
1677 	}
1678 
1679 	if (members_overview_enabled && se->show_members_overview()) {
1680 		members_overview_alphabeta_sort_button->set_visible(true);
1681 		members_overview->set_visible(true);
1682 		overview_vbox->set_visible(true);
1683 	} else {
1684 		members_overview_alphabeta_sort_button->set_visible(false);
1685 		members_overview->set_visible(false);
1686 		overview_vbox->set_visible(false);
1687 	}
1688 }
1689 
_toggle_members_overview_alpha_sort(bool p_alphabetic_sort)1690 void ScriptEditor::_toggle_members_overview_alpha_sort(bool p_alphabetic_sort) {
1691 	EditorSettings::get_singleton()->set("text_editor/tools/sort_members_outline_alphabetically", p_alphabetic_sort);
1692 	_update_members_overview();
1693 }
1694 
_update_members_overview()1695 void ScriptEditor::_update_members_overview() {
1696 	members_overview->clear();
1697 
1698 	ScriptEditorBase *se = _get_current_editor();
1699 	if (!se) {
1700 		return;
1701 	}
1702 
1703 	Vector<String> functions = se->get_functions();
1704 	if (EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically")) {
1705 		functions.sort();
1706 	}
1707 
1708 	for (int i = 0; i < functions.size(); i++) {
1709 		String filter = filter_methods->get_text();
1710 		String name = functions[i].get_slice(":", 0);
1711 		if (filter == "" || filter.is_subsequence_ofi(name)) {
1712 			members_overview->add_item(name);
1713 			members_overview->set_item_metadata(members_overview->get_item_count() - 1, functions[i].get_slice(":", 1).to_int() - 1);
1714 		}
1715 	}
1716 
1717 	String path = se->get_edited_resource()->get_path();
1718 	bool built_in = !path.is_resource_file();
1719 	String name = built_in ? path.get_file() : se->get_name();
1720 	filename->set_text(name);
1721 }
1722 
_update_help_overview_visibility()1723 void ScriptEditor::_update_help_overview_visibility() {
1724 
1725 	int selected = tab_container->get_current_tab();
1726 	if (selected < 0 || selected >= tab_container->get_child_count()) {
1727 		help_overview->set_visible(false);
1728 		return;
1729 	}
1730 
1731 	Node *current = tab_container->get_child(tab_container->get_current_tab());
1732 	EditorHelp *se = Object::cast_to<EditorHelp>(current);
1733 	if (!se) {
1734 		help_overview->set_visible(false);
1735 		return;
1736 	}
1737 
1738 	if (help_overview_enabled) {
1739 		members_overview_alphabeta_sort_button->set_visible(false);
1740 		help_overview->set_visible(true);
1741 		overview_vbox->set_visible(true);
1742 		filename->set_text(se->get_name());
1743 	} else {
1744 		help_overview->set_visible(false);
1745 		overview_vbox->set_visible(false);
1746 	}
1747 }
1748 
_update_help_overview()1749 void ScriptEditor::_update_help_overview() {
1750 	help_overview->clear();
1751 
1752 	int selected = tab_container->get_current_tab();
1753 	if (selected < 0 || selected >= tab_container->get_child_count())
1754 		return;
1755 
1756 	Node *current = tab_container->get_child(tab_container->get_current_tab());
1757 	EditorHelp *se = Object::cast_to<EditorHelp>(current);
1758 	if (!se) {
1759 		return;
1760 	}
1761 
1762 	Vector<Pair<String, int> > sections = se->get_sections();
1763 	for (int i = 0; i < sections.size(); i++) {
1764 		help_overview->add_item(sections[i].first);
1765 		help_overview->set_item_metadata(i, sections[i].second);
1766 	}
1767 }
1768 
_update_script_colors()1769 void ScriptEditor::_update_script_colors() {
1770 
1771 	bool script_temperature_enabled = EditorSettings::get_singleton()->get("text_editor/script_list/script_temperature_enabled");
1772 	bool highlight_current = EditorSettings::get_singleton()->get("text_editor/script_list/highlight_current_script");
1773 
1774 	int hist_size = EditorSettings::get_singleton()->get("text_editor/script_list/script_temperature_history_size");
1775 	Color hot_color = get_color("accent_color", "Editor");
1776 	Color cold_color = get_color("font_color", "Editor");
1777 
1778 	for (int i = 0; i < script_list->get_item_count(); i++) {
1779 
1780 		int c = script_list->get_item_metadata(i);
1781 		Node *n = tab_container->get_child(c);
1782 		if (!n)
1783 			continue;
1784 
1785 		script_list->set_item_custom_bg_color(i, Color(0, 0, 0, 0));
1786 
1787 		bool current = tab_container->get_current_tab() == c;
1788 		if (current && highlight_current) {
1789 			script_list->set_item_custom_bg_color(i, EditorSettings::get_singleton()->get("text_editor/script_list/current_script_background_color"));
1790 
1791 		} else if (script_temperature_enabled) {
1792 
1793 			if (!n->has_meta("__editor_pass")) {
1794 				continue;
1795 			}
1796 
1797 			int pass = n->get_meta("__editor_pass");
1798 			int h = edit_pass - pass;
1799 			if (h > hist_size) {
1800 				continue;
1801 			}
1802 			int non_zero_hist_size = (hist_size == 0) ? 1 : hist_size;
1803 			float v = Math::ease((edit_pass - pass) / float(non_zero_hist_size), 0.4);
1804 
1805 			script_list->set_item_custom_fg_color(i, hot_color.linear_interpolate(cold_color, v));
1806 		}
1807 	}
1808 }
1809 
_update_script_names()1810 void ScriptEditor::_update_script_names() {
1811 
1812 	if (restoring_layout)
1813 		return;
1814 
1815 	Set<Ref<Script> > used;
1816 	Node *edited = EditorNode::get_singleton()->get_edited_scene();
1817 	if (edited) {
1818 		_find_scripts(edited, edited, used);
1819 	}
1820 
1821 	script_list->clear();
1822 	bool split_script_help = EditorSettings::get_singleton()->get("text_editor/script_list/group_help_pages");
1823 	ScriptSortBy sort_by = (ScriptSortBy)(int)EditorSettings::get_singleton()->get("text_editor/script_list/sort_scripts_by");
1824 	ScriptListName display_as = (ScriptListName)(int)EditorSettings::get_singleton()->get("text_editor/script_list/list_script_names_as");
1825 
1826 	Vector<_ScriptEditorItemData> sedata;
1827 
1828 	for (int i = 0; i < tab_container->get_child_count(); i++) {
1829 
1830 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
1831 		if (se) {
1832 
1833 			Ref<Texture> icon = se->get_icon();
1834 			String path = se->get_edited_resource()->get_path();
1835 			bool built_in = !path.is_resource_file();
1836 			String name;
1837 
1838 			if (built_in) {
1839 
1840 				name = path.get_file();
1841 				const String &resource_name = se->get_edited_resource()->get_name();
1842 				if (resource_name != "") {
1843 					// If the built-in script has a custom resource name defined,
1844 					// display the built-in script name as follows: `ResourceName (scene_file.tscn)`
1845 					name = vformat("%s (%s)", resource_name, name.substr(0, name.find("::", 0)));
1846 				}
1847 			} else {
1848 
1849 				name = se->get_name();
1850 			}
1851 
1852 			_ScriptEditorItemData sd;
1853 			sd.icon = icon;
1854 			sd.name = name;
1855 			sd.tooltip = path;
1856 			sd.index = i;
1857 			sd.used = used.has(se->get_edited_resource());
1858 			sd.category = 0;
1859 			sd.ref = se;
1860 
1861 			switch (sort_by) {
1862 				case SORT_BY_NAME: {
1863 					sd.sort_key = name.to_lower();
1864 				} break;
1865 				case SORT_BY_PATH: {
1866 					sd.sort_key = path;
1867 				} break;
1868 				case SORT_BY_NONE: {
1869 					sd.sort_key = "";
1870 				} break;
1871 			}
1872 
1873 			switch (display_as) {
1874 				case DISPLAY_NAME: {
1875 					sd.name = name;
1876 				} break;
1877 				case DISPLAY_DIR_AND_NAME: {
1878 					if (!path.get_base_dir().get_file().empty()) {
1879 						sd.name = path.get_base_dir().get_file().plus_file(name);
1880 					} else {
1881 						sd.name = name;
1882 					}
1883 				} break;
1884 				case DISPLAY_FULL_PATH: {
1885 					sd.name = path;
1886 				} break;
1887 			}
1888 
1889 			sedata.push_back(sd);
1890 		}
1891 
1892 		Vector<String> disambiguated_script_names;
1893 		Vector<String> full_script_paths;
1894 		for (int j = 0; j < sedata.size(); j++) {
1895 			disambiguated_script_names.push_back(sedata[j].name.replace("(*)", ""));
1896 			full_script_paths.push_back(sedata[j].tooltip);
1897 		}
1898 
1899 		EditorNode::disambiguate_filenames(full_script_paths, disambiguated_script_names);
1900 
1901 		for (int j = 0; j < sedata.size(); j++) {
1902 			if (sedata[j].name.ends_with("(*)")) {
1903 				sedata.write[j].name = disambiguated_script_names[j] + "(*)";
1904 			} else {
1905 				sedata.write[j].name = disambiguated_script_names[j];
1906 			}
1907 		}
1908 
1909 		EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
1910 		if (eh) {
1911 
1912 			String name = eh->get_class();
1913 			Ref<Texture> icon = get_icon("Help", "EditorIcons");
1914 			String tooltip = vformat(TTR("%s Class Reference"), name);
1915 
1916 			_ScriptEditorItemData sd;
1917 			sd.icon = icon;
1918 			sd.name = name;
1919 			sd.sort_key = name.to_lower();
1920 			sd.tooltip = tooltip;
1921 			sd.index = i;
1922 			sd.used = false;
1923 			sd.category = split_script_help ? 1 : 0;
1924 			sd.ref = eh;
1925 
1926 			sedata.push_back(sd);
1927 		}
1928 	}
1929 
1930 	if (_sort_list_on_update && !sedata.empty()) {
1931 		sedata.sort();
1932 
1933 		// change actual order of tab_container so that the order can be rearranged by user
1934 		int cur_tab = tab_container->get_current_tab();
1935 		int prev_tab = tab_container->get_previous_tab();
1936 		int new_cur_tab = -1;
1937 		int new_prev_tab = -1;
1938 		for (int i = 0; i < sedata.size(); i++) {
1939 			tab_container->move_child(sedata[i].ref, i);
1940 			if (new_prev_tab == -1 && sedata[i].index == prev_tab) {
1941 				new_prev_tab = i;
1942 			}
1943 			if (new_cur_tab == -1 && sedata[i].index == cur_tab) {
1944 				new_cur_tab = i;
1945 			}
1946 			// Update index of sd entries for sorted order
1947 			_ScriptEditorItemData sd = sedata[i];
1948 			sd.index = i;
1949 			sedata.set(i, sd);
1950 		}
1951 		tab_container->set_current_tab(new_prev_tab);
1952 		tab_container->set_current_tab(new_cur_tab);
1953 		_sort_list_on_update = false;
1954 	}
1955 
1956 	Vector<_ScriptEditorItemData> sedata_filtered;
1957 	for (int i = 0; i < sedata.size(); i++) {
1958 		String filter = filter_scripts->get_text();
1959 		if (filter == "" || filter.is_subsequence_ofi(sedata[i].name)) {
1960 			sedata_filtered.push_back(sedata[i]);
1961 		}
1962 	}
1963 
1964 	for (int i = 0; i < sedata_filtered.size(); i++) {
1965 		script_list->add_item(sedata_filtered[i].name, sedata_filtered[i].icon);
1966 		int index = script_list->get_item_count() - 1;
1967 		script_list->set_item_tooltip(index, sedata_filtered[i].tooltip);
1968 		script_list->set_item_metadata(index, sedata_filtered[i].index); /* Saving as metadata the script's index in the tab container and not the filtered one */
1969 		if (sedata_filtered[i].used) {
1970 			script_list->set_item_custom_bg_color(index, Color(88 / 255.0, 88 / 255.0, 60 / 255.0));
1971 		}
1972 		if (tab_container->get_current_tab() == sedata_filtered[i].index) {
1973 			script_list->select(index);
1974 			script_name_label->set_text(sedata_filtered[i].name);
1975 			script_icon->set_texture(sedata_filtered[i].icon);
1976 		}
1977 	}
1978 
1979 	if (!waiting_update_names) {
1980 		_update_members_overview();
1981 		_update_help_overview();
1982 	} else {
1983 		waiting_update_names = false;
1984 	}
1985 	_update_members_overview_visibility();
1986 	_update_help_overview_visibility();
1987 	_update_script_colors();
1988 
1989 	file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(FILE_REOPEN_CLOSED), previous_scripts.empty());
1990 }
1991 
_update_script_connections()1992 void ScriptEditor::_update_script_connections() {
1993 	for (int i = 0; i < tab_container->get_child_count(); i++) {
1994 		ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(tab_container->get_child(i));
1995 		if (!ste) {
1996 			continue;
1997 		}
1998 		ste->_update_connected_methods();
1999 	}
2000 }
2001 
_load_text_file(const String & p_path,Error * r_error)2002 Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) {
2003 	if (r_error) {
2004 		*r_error = ERR_FILE_CANT_OPEN;
2005 	}
2006 
2007 	String local_path = ProjectSettings::get_singleton()->localize_path(p_path);
2008 	String path = ResourceLoader::path_remap(local_path);
2009 
2010 	TextFile *text_file = memnew(TextFile);
2011 	Ref<TextFile> text_res(text_file);
2012 	Error err = text_file->load_text(path);
2013 
2014 	ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load text file '" + path + "'.");
2015 
2016 	text_file->set_file_path(local_path);
2017 	text_file->set_path(local_path, true);
2018 
2019 	if (ResourceLoader::get_timestamp_on_load()) {
2020 		text_file->set_last_modified_time(FileAccess::get_modified_time(path));
2021 	}
2022 
2023 	if (r_error) {
2024 		*r_error = OK;
2025 	}
2026 
2027 	return text_res;
2028 }
2029 
_save_text_file(Ref<TextFile> p_text_file,const String & p_path)2030 Error ScriptEditor::_save_text_file(Ref<TextFile> p_text_file, const String &p_path) {
2031 	Ref<TextFile> sqscr = p_text_file;
2032 	ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);
2033 
2034 	String source = sqscr->get_text();
2035 
2036 	Error err;
2037 	FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err);
2038 
2039 	ERR_FAIL_COND_V_MSG(err, err, "Cannot save text file '" + p_path + "'.");
2040 
2041 	file->store_string(source);
2042 	if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
2043 		memdelete(file);
2044 		return ERR_CANT_CREATE;
2045 	}
2046 	file->close();
2047 	memdelete(file);
2048 
2049 	if (ResourceSaver::get_timestamp_on_save()) {
2050 		p_text_file->set_last_modified_time(FileAccess::get_modified_time(p_path));
2051 	}
2052 
2053 	_res_saved_callback(sqscr);
2054 	return OK;
2055 }
2056 
edit(const RES & p_resource,int p_line,int p_col,bool p_grab_focus)2057 bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_grab_focus) {
2058 
2059 	if (p_resource.is_null())
2060 		return false;
2061 
2062 	Ref<Script> script = p_resource;
2063 
2064 	// Don't open dominant script if using an external editor.
2065 	const bool use_external_editor =
2066 			EditorSettings::get_singleton()->get("text_editor/external/use_external_editor") ||
2067 			(script.is_valid() && script->get_language()->overrides_external_editor());
2068 	const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/files/open_dominant_script_on_scene_change");
2069 
2070 	const bool should_open = (open_dominant && !use_external_editor) || !EditorNode::get_singleton()->is_changing_scene();
2071 
2072 	if (script.is_valid() && script->get_language()->overrides_external_editor()) {
2073 		if (should_open) {
2074 			Error err = script->get_language()->open_in_external_editor(script, p_line >= 0 ? p_line : 0, p_col);
2075 			if (err != OK)
2076 				ERR_PRINT("Couldn't open script in the overridden external text editor");
2077 		}
2078 		return false;
2079 	}
2080 
2081 	if (use_external_editor &&
2082 			(debugger->get_dump_stack_script() != p_resource || debugger->get_debug_with_external_editor()) &&
2083 			p_resource->get_path().is_resource_file() &&
2084 			p_resource->get_class_name() != StringName("VisualScript")) {
2085 		String path = EditorSettings::get_singleton()->get("text_editor/external/exec_path");
2086 		String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags");
2087 
2088 		List<String> args;
2089 		bool has_file_flag = false;
2090 		String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path());
2091 
2092 		if (flags.size()) {
2093 			String project_path = ProjectSettings::get_singleton()->get_resource_path();
2094 
2095 			flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0));
2096 			flags = flags.replacen("{col}", itos(p_col));
2097 			flags = flags.strip_edges().replace("\\\\", "\\");
2098 
2099 			int from = 0;
2100 			int num_chars = 0;
2101 			bool inside_quotes = false;
2102 
2103 			for (int i = 0; i < flags.size(); i++) {
2104 
2105 				if (flags[i] == '"' && (!i || flags[i - 1] != '\\')) {
2106 
2107 					if (!inside_quotes) {
2108 						from++;
2109 					}
2110 					inside_quotes = !inside_quotes;
2111 
2112 				} else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) {
2113 
2114 					String arg = flags.substr(from, num_chars);
2115 					if (arg.find("{file}") != -1) {
2116 						has_file_flag = true;
2117 					}
2118 
2119 					// do path replacement here, else there will be issues with spaces and quotes
2120 					arg = arg.replacen("{project}", project_path);
2121 					arg = arg.replacen("{file}", script_path);
2122 					args.push_back(arg);
2123 
2124 					from = i + 1;
2125 					num_chars = 0;
2126 				} else {
2127 					num_chars++;
2128 				}
2129 			}
2130 		}
2131 
2132 		// Default to passing script path if no {file} flag is specified.
2133 		if (!has_file_flag) {
2134 			args.push_back(script_path);
2135 		}
2136 
2137 		Error err = OS::get_singleton()->execute(path, args, false);
2138 		if (err == OK)
2139 			return false;
2140 		WARN_PRINT("Couldn't open external text editor, using internal");
2141 	}
2142 
2143 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2144 
2145 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2146 		if (!se)
2147 			continue;
2148 
2149 		if ((script != NULL && se->get_edited_resource() == p_resource) || se->get_edited_resource()->get_path() == p_resource->get_path()) {
2150 
2151 			if (should_open) {
2152 				if (tab_container->get_current_tab() != i) {
2153 					_go_to_tab(i);
2154 					_update_script_names();
2155 				}
2156 				if (is_visible_in_tree())
2157 					se->ensure_focus();
2158 
2159 				if (p_line > 0) {
2160 					se->goto_line(p_line - 1);
2161 				}
2162 			}
2163 			_update_script_names();
2164 			script_list->ensure_current_is_visible();
2165 			return true;
2166 		}
2167 	}
2168 
2169 	// doesn't have it, make a new one
2170 
2171 	ScriptEditorBase *se = NULL;
2172 
2173 	for (int i = script_editor_func_count - 1; i >= 0; i--) {
2174 		se = script_editor_funcs[i](p_resource);
2175 		if (se)
2176 			break;
2177 	}
2178 	ERR_FAIL_COND_V(!se, false);
2179 
2180 	if (p_resource->get_class_name() != StringName("VisualScript")) {
2181 		bool highlighter_set = false;
2182 		for (int i = 0; i < syntax_highlighters_func_count; i++) {
2183 			SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i]();
2184 			se->add_syntax_highlighter(highlighter);
2185 
2186 			if (script != NULL && !highlighter_set) {
2187 				List<String> languages = highlighter->get_supported_languages();
2188 				if (languages.find(script->get_language()->get_name())) {
2189 					se->set_syntax_highlighter(highlighter);
2190 					highlighter_set = true;
2191 				}
2192 			}
2193 		}
2194 	}
2195 
2196 	tab_container->add_child(se);
2197 	se->set_edited_resource(p_resource);
2198 	se->set_tooltip_request_func("_get_debug_tooltip", this);
2199 	if (se->get_edit_menu()) {
2200 		se->get_edit_menu()->hide();
2201 		menu_hb->add_child(se->get_edit_menu());
2202 		menu_hb->move_child(se->get_edit_menu(), 1);
2203 	}
2204 
2205 	if (p_grab_focus) {
2206 		_go_to_tab(tab_container->get_tab_count() - 1);
2207 	}
2208 
2209 	_sort_list_on_update = true;
2210 	_update_script_names();
2211 	_save_layout();
2212 	se->connect("name_changed", this, "_update_script_names");
2213 	se->connect("edited_script_changed", this, "_script_changed");
2214 	se->connect("request_help", this, "_help_search");
2215 	se->connect("request_open_script_at_line", this, "_goto_script_line");
2216 	se->connect("go_to_help", this, "_help_class_goto");
2217 	se->connect("request_save_history", this, "_save_history");
2218 	se->connect("search_in_files_requested", this, "_on_find_in_files_requested");
2219 
2220 	//test for modification, maybe the script was not edited but was loaded
2221 
2222 	_test_script_times_on_disk(p_resource);
2223 	_update_modified_scripts_for_external_editor(p_resource);
2224 
2225 	if (p_line > 0) {
2226 		se->goto_line(p_line - 1);
2227 	}
2228 
2229 	notify_script_changed(p_resource);
2230 	_add_recent_script(p_resource->get_path());
2231 	return true;
2232 }
2233 
save_all_scripts()2234 void ScriptEditor::save_all_scripts() {
2235 
2236 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2237 
2238 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2239 		if (!se)
2240 			continue;
2241 
2242 		if (convert_indent_on_save) {
2243 			if (use_space_indentation) {
2244 				se->convert_indent_to_spaces();
2245 			} else {
2246 				se->convert_indent_to_tabs();
2247 			}
2248 		}
2249 
2250 		if (trim_trailing_whitespace_on_save) {
2251 			se->trim_trailing_whitespace();
2252 		}
2253 
2254 		se->insert_final_newline();
2255 
2256 		if (!se->is_unsaved())
2257 			continue;
2258 
2259 		RES edited_res = se->get_edited_resource();
2260 		if (edited_res.is_valid()) {
2261 			se->apply_code();
2262 		}
2263 
2264 		if (edited_res->get_path() != "" && edited_res->get_path().find("local://") == -1 && edited_res->get_path().find("::") == -1) {
2265 			Ref<TextFile> text_file = edited_res;
2266 			if (text_file != NULL) {
2267 				_save_text_file(text_file, text_file->get_path());
2268 				continue;
2269 			}
2270 			editor->save_resource(edited_res); //external script, save it
2271 		}
2272 	}
2273 
2274 	_update_script_names();
2275 	EditorFileSystem::get_singleton()->update_script_classes();
2276 }
2277 
apply_scripts() const2278 void ScriptEditor::apply_scripts() const {
2279 
2280 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2281 
2282 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2283 		if (!se)
2284 			continue;
2285 		se->apply_code();
2286 	}
2287 }
2288 
open_script_create_dialog(const String & p_base_name,const String & p_base_path)2289 void ScriptEditor::open_script_create_dialog(const String &p_base_name, const String &p_base_path) {
2290 	_menu_option(FILE_NEW);
2291 	script_create_dialog->config(p_base_name, p_base_path);
2292 }
2293 
_editor_play()2294 void ScriptEditor::_editor_play() {
2295 
2296 	debugger->start();
2297 	debug_menu->get_popup()->grab_focus();
2298 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
2299 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
2300 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), false);
2301 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
2302 }
2303 
_editor_pause()2304 void ScriptEditor::_editor_pause() {
2305 }
_editor_stop()2306 void ScriptEditor::_editor_stop() {
2307 
2308 	debugger->stop();
2309 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
2310 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
2311 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true);
2312 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
2313 
2314 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2315 
2316 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2317 		if (!se) {
2318 
2319 			continue;
2320 		}
2321 
2322 		se->set_debugger_active(false);
2323 	}
2324 }
2325 
_add_callback(Object * p_obj,const String & p_function,const PoolStringArray & p_args)2326 void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const PoolStringArray &p_args) {
2327 
2328 	ERR_FAIL_COND(!p_obj);
2329 	Ref<Script> script = p_obj->get_script();
2330 	ERR_FAIL_COND(!script.is_valid());
2331 
2332 	editor->push_item(script.ptr());
2333 
2334 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2335 
2336 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2337 		if (!se)
2338 			continue;
2339 		if (se->get_edited_resource() != script)
2340 			continue;
2341 
2342 		se->add_callback(p_function, p_args);
2343 
2344 		_go_to_tab(i);
2345 
2346 		script_list->select(script_list->find_metadata(i));
2347 
2348 		break;
2349 	}
2350 }
2351 
_save_layout()2352 void ScriptEditor::_save_layout() {
2353 
2354 	if (restoring_layout) {
2355 		return;
2356 	}
2357 
2358 	editor->save_layout();
2359 }
2360 
_editor_settings_changed()2361 void ScriptEditor::_editor_settings_changed() {
2362 
2363 	trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");
2364 	convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/indent/convert_indent_on_save");
2365 	use_space_indentation = EditorSettings::get_singleton()->get("text_editor/indent/type");
2366 
2367 	members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/script_list/show_members_overview");
2368 	help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index");
2369 	_update_members_overview_visibility();
2370 	_update_help_overview_visibility();
2371 
2372 	_update_autosave_timer();
2373 
2374 	if (current_theme == "") {
2375 		current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
2376 	} else if (current_theme != EditorSettings::get_singleton()->get("text_editor/theme/color_theme")) {
2377 		current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
2378 		EditorSettings::get_singleton()->load_text_editor_theme();
2379 	}
2380 
2381 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2382 
2383 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2384 		if (!se)
2385 			continue;
2386 
2387 		se->update_settings();
2388 	}
2389 	_update_script_colors();
2390 	_update_script_names();
2391 
2392 	ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/files/auto_reload_and_parse_scripts_on_save", true));
2393 }
2394 
_autosave_scripts()2395 void ScriptEditor::_autosave_scripts() {
2396 
2397 	save_all_scripts();
2398 }
2399 
_update_autosave_timer()2400 void ScriptEditor::_update_autosave_timer() {
2401 
2402 	if (!autosave_timer->is_inside_tree()) {
2403 		return;
2404 	}
2405 
2406 	float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs");
2407 	if (autosave_time > 0) {
2408 		autosave_timer->set_wait_time(autosave_time);
2409 		autosave_timer->start();
2410 	} else {
2411 		autosave_timer->stop();
2412 	}
2413 }
2414 
_tree_changed()2415 void ScriptEditor::_tree_changed() {
2416 
2417 	if (waiting_update_names)
2418 		return;
2419 
2420 	waiting_update_names = true;
2421 	call_deferred("_update_script_names");
2422 	call_deferred("_update_script_connections");
2423 }
2424 
_script_split_dragged(float)2425 void ScriptEditor::_script_split_dragged(float) {
2426 
2427 	_save_layout();
2428 }
2429 
get_drag_data_fw(const Point2 & p_point,Control * p_from)2430 Variant ScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
2431 
2432 	if (tab_container->get_child_count() == 0)
2433 		return Variant();
2434 
2435 	Node *cur_node = tab_container->get_child(tab_container->get_current_tab());
2436 
2437 	HBoxContainer *drag_preview = memnew(HBoxContainer);
2438 	String preview_name = "";
2439 	Ref<Texture> preview_icon;
2440 
2441 	ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(cur_node);
2442 	if (se) {
2443 		preview_name = se->get_name();
2444 		preview_icon = se->get_icon();
2445 	}
2446 	EditorHelp *eh = Object::cast_to<EditorHelp>(cur_node);
2447 	if (eh) {
2448 		preview_name = eh->get_class();
2449 		preview_icon = get_icon("Help", "EditorIcons");
2450 	}
2451 
2452 	if (!preview_icon.is_null()) {
2453 		TextureRect *tf = memnew(TextureRect);
2454 		tf->set_texture(preview_icon);
2455 		drag_preview->add_child(tf);
2456 	}
2457 	Label *label = memnew(Label(preview_name));
2458 	drag_preview->add_child(label);
2459 	set_drag_preview(drag_preview);
2460 
2461 	Dictionary drag_data;
2462 	drag_data["type"] = "script_list_element"; // using a custom type because node caused problems when dragging to scene tree
2463 	drag_data["script_list_element"] = cur_node;
2464 
2465 	return drag_data;
2466 }
2467 
can_drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from) const2468 bool ScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
2469 
2470 	Dictionary d = p_data;
2471 	if (!d.has("type"))
2472 		return false;
2473 
2474 	if (String(d["type"]) == "script_list_element") {
2475 
2476 		Node *node = d["script_list_element"];
2477 
2478 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
2479 		if (se) {
2480 			return true;
2481 		}
2482 		EditorHelp *eh = Object::cast_to<EditorHelp>(node);
2483 		if (eh) {
2484 			return true;
2485 		}
2486 	}
2487 
2488 	if (String(d["type"]) == "nodes") {
2489 
2490 		Array nodes = d["nodes"];
2491 		if (nodes.size() == 0)
2492 			return false;
2493 		Node *node = get_node((nodes[0]));
2494 
2495 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
2496 		if (se) {
2497 			return true;
2498 		}
2499 		EditorHelp *eh = Object::cast_to<EditorHelp>(node);
2500 		if (eh) {
2501 			return true;
2502 		}
2503 	}
2504 
2505 	if (String(d["type"]) == "files") {
2506 
2507 		Vector<String> files = d["files"];
2508 
2509 		if (files.size() == 0)
2510 			return false; //weird
2511 
2512 		for (int i = 0; i < files.size(); i++) {
2513 			String file = files[i];
2514 			if (file == "" || !FileAccess::exists(file))
2515 				continue;
2516 			Ref<Script> scr = ResourceLoader::load(file);
2517 			if (scr.is_valid()) {
2518 				return true;
2519 			}
2520 		}
2521 		return true;
2522 	}
2523 
2524 	return false;
2525 }
2526 
drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from)2527 void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
2528 
2529 	if (!can_drop_data_fw(p_point, p_data, p_from))
2530 		return;
2531 
2532 	Dictionary d = p_data;
2533 	if (!d.has("type"))
2534 		return;
2535 
2536 	if (String(d["type"]) == "script_list_element") {
2537 
2538 		Node *node = d["script_list_element"];
2539 
2540 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
2541 		EditorHelp *eh = Object::cast_to<EditorHelp>(node);
2542 		if (se || eh) {
2543 			int new_index = 0;
2544 			if (script_list->get_item_count() > 0) {
2545 				new_index = script_list->get_item_metadata(script_list->get_item_at_position(p_point));
2546 			}
2547 			tab_container->move_child(node, new_index);
2548 			tab_container->set_current_tab(new_index);
2549 			_update_script_names();
2550 		}
2551 	}
2552 
2553 	if (String(d["type"]) == "nodes") {
2554 
2555 		Array nodes = d["nodes"];
2556 		if (nodes.size() == 0)
2557 			return;
2558 		Node *node = get_node(nodes[0]);
2559 
2560 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
2561 		EditorHelp *eh = Object::cast_to<EditorHelp>(node);
2562 		if (se || eh) {
2563 			int new_index = 0;
2564 			if (script_list->get_item_count() > 0) {
2565 				new_index = script_list->get_item_metadata(script_list->get_item_at_position(p_point));
2566 			}
2567 			tab_container->move_child(node, new_index);
2568 			tab_container->set_current_tab(new_index);
2569 			_update_script_names();
2570 		}
2571 	}
2572 
2573 	if (String(d["type"]) == "files") {
2574 
2575 		Vector<String> files = d["files"];
2576 
2577 		int new_index = 0;
2578 		if (script_list->get_item_count() > 0) {
2579 			new_index = script_list->get_item_metadata(script_list->get_item_at_position(p_point));
2580 		}
2581 		int num_tabs_before = tab_container->get_child_count();
2582 		for (int i = 0; i < files.size(); i++) {
2583 			String file = files[i];
2584 			if (file == "" || !FileAccess::exists(file))
2585 				continue;
2586 			Ref<Script> scr = ResourceLoader::load(file);
2587 			if (scr.is_valid()) {
2588 				edit(scr);
2589 				if (tab_container->get_child_count() > num_tabs_before) {
2590 					tab_container->move_child(tab_container->get_child(tab_container->get_child_count() - 1), new_index);
2591 					num_tabs_before = tab_container->get_child_count();
2592 				} else { /* Maybe script was already open */
2593 					tab_container->move_child(tab_container->get_child(tab_container->get_current_tab()), new_index);
2594 				}
2595 			}
2596 		}
2597 		tab_container->set_current_tab(new_index);
2598 		_update_script_names();
2599 	}
2600 }
2601 
_unhandled_input(const Ref<InputEvent> & p_event)2602 void ScriptEditor::_unhandled_input(const Ref<InputEvent> &p_event) {
2603 	if (!is_visible_in_tree() || !p_event->is_pressed() || p_event->is_echo())
2604 		return;
2605 	if (ED_IS_SHORTCUT("script_editor/next_script", p_event)) {
2606 		if (script_list->get_item_count() > 1) {
2607 			int next_tab = script_list->get_current() + 1;
2608 			next_tab %= script_list->get_item_count();
2609 			_go_to_tab(script_list->get_item_metadata(next_tab));
2610 			_update_script_names();
2611 		}
2612 	}
2613 	if (ED_IS_SHORTCUT("script_editor/prev_script", p_event)) {
2614 		if (script_list->get_item_count() > 1) {
2615 			int next_tab = script_list->get_current() - 1;
2616 			next_tab = next_tab >= 0 ? next_tab : script_list->get_item_count() - 1;
2617 			_go_to_tab(script_list->get_item_metadata(next_tab));
2618 			_update_script_names();
2619 		}
2620 	}
2621 	if (ED_IS_SHORTCUT("script_editor/window_move_up", p_event)) {
2622 		_menu_option(WINDOW_MOVE_UP);
2623 	}
2624 	if (ED_IS_SHORTCUT("script_editor/window_move_down", p_event)) {
2625 		_menu_option(WINDOW_MOVE_DOWN);
2626 	}
2627 }
2628 
_script_list_gui_input(const Ref<InputEvent> & ev)2629 void ScriptEditor::_script_list_gui_input(const Ref<InputEvent> &ev) {
2630 
2631 	Ref<InputEventMouseButton> mb = ev;
2632 	if (mb.is_valid() && mb->is_pressed()) {
2633 		switch (mb->get_button_index()) {
2634 
2635 			case BUTTON_MIDDLE: {
2636 				// Right-click selects automatically; middle-click does not.
2637 				int idx = script_list->get_item_at_position(mb->get_position(), true);
2638 				if (idx >= 0) {
2639 					script_list->select(idx);
2640 					_script_selected(idx);
2641 					_menu_option(FILE_CLOSE);
2642 				}
2643 			} break;
2644 
2645 			case BUTTON_RIGHT: {
2646 				_make_script_list_context_menu();
2647 			} break;
2648 		}
2649 	}
2650 }
2651 
_make_script_list_context_menu()2652 void ScriptEditor::_make_script_list_context_menu() {
2653 
2654 	context_menu->clear();
2655 
2656 	int selected = tab_container->get_current_tab();
2657 	if (selected < 0 || selected >= tab_container->get_child_count())
2658 		return;
2659 
2660 	ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
2661 	if (se) {
2662 		context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save"), FILE_SAVE);
2663 		context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save_as"), FILE_SAVE_AS);
2664 	}
2665 	context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE);
2666 	context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_all"), CLOSE_ALL);
2667 	context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_other_tabs"), CLOSE_OTHER_TABS);
2668 	context_menu->add_separator();
2669 	if (se) {
2670 		Ref<Script> scr = se->get_edited_resource();
2671 		if (scr != NULL) {
2672 			context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT);
2673 			if (!scr.is_null() && scr->is_tool()) {
2674 				context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/run_file"), FILE_RUN);
2675 				context_menu->add_separator();
2676 			}
2677 		}
2678 		context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/copy_path"), FILE_COPY_PATH);
2679 		context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/show_in_file_system"), SHOW_IN_FILE_SYSTEM);
2680 		context_menu->add_separator();
2681 	}
2682 
2683 	context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_up"), WINDOW_MOVE_UP);
2684 	context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_down"), WINDOW_MOVE_DOWN);
2685 	context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_sort"), WINDOW_SORT);
2686 	context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/toggle_scripts_panel"), TOGGLE_SCRIPTS_PANEL);
2687 
2688 	context_menu->set_position(get_global_transform().xform(get_local_mouse_position()));
2689 	context_menu->set_size(Vector2(1, 1));
2690 	context_menu->popup();
2691 }
2692 
set_window_layout(Ref<ConfigFile> p_layout)2693 void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
2694 
2695 	if (!bool(EDITOR_DEF("text_editor/files/restore_scripts_on_load", true))) {
2696 		return;
2697 	}
2698 
2699 	if (!p_layout->has_section_key("ScriptEditor", "open_scripts") && !p_layout->has_section_key("ScriptEditor", "open_help"))
2700 		return;
2701 
2702 	Array scripts = p_layout->get_value("ScriptEditor", "open_scripts");
2703 	Array helps;
2704 	if (p_layout->has_section_key("ScriptEditor", "open_help"))
2705 		helps = p_layout->get_value("ScriptEditor", "open_help");
2706 
2707 	restoring_layout = true;
2708 
2709 	List<String> extensions;
2710 	ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
2711 
2712 	for (int i = 0; i < scripts.size(); i++) {
2713 
2714 		String path = scripts[i];
2715 
2716 		Dictionary script_info = scripts[i];
2717 		if (!script_info.empty()) {
2718 			path = script_info["path"];
2719 		}
2720 
2721 		if (!FileAccess::exists(path))
2722 			continue;
2723 
2724 		if (extensions.find(path.get_extension())) {
2725 			Ref<Script> scr = ResourceLoader::load(path);
2726 			if (!scr.is_valid()) {
2727 				continue;
2728 			}
2729 			if (!edit(scr)) {
2730 				continue;
2731 			}
2732 		} else {
2733 			Error error;
2734 			Ref<TextFile> text_file = _load_text_file(path, &error);
2735 			if (error != OK || !text_file.is_valid()) {
2736 				continue;
2737 			}
2738 			if (!edit(text_file)) {
2739 				continue;
2740 			}
2741 		}
2742 
2743 		if (!script_info.empty()) {
2744 			ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_tab_count() - 1));
2745 			if (se) {
2746 				se->set_edit_state(script_info["state"]);
2747 			}
2748 		}
2749 	}
2750 
2751 	for (int i = 0; i < helps.size(); i++) {
2752 
2753 		String path = helps[i];
2754 		if (path == "") { // invalid, skip
2755 			continue;
2756 		}
2757 		_help_class_open(path);
2758 	}
2759 
2760 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2761 		tab_container->get_child(i)->set_meta("__editor_pass", Variant());
2762 	}
2763 
2764 	if (p_layout->has_section_key("ScriptEditor", "split_offset")) {
2765 		script_split->set_split_offset(p_layout->get_value("ScriptEditor", "split_offset"));
2766 	}
2767 
2768 	restoring_layout = false;
2769 
2770 	_update_script_names();
2771 }
2772 
get_window_layout(Ref<ConfigFile> p_layout)2773 void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
2774 
2775 	Array scripts;
2776 	Array helps;
2777 
2778 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2779 
2780 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2781 		if (se) {
2782 
2783 			String path = se->get_edited_resource()->get_path();
2784 			if (!path.is_resource_file())
2785 				continue;
2786 
2787 			Dictionary script_info;
2788 			script_info["path"] = path;
2789 			script_info["state"] = se->get_edit_state();
2790 
2791 			scripts.push_back(script_info);
2792 		}
2793 
2794 		EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
2795 
2796 		if (eh) {
2797 
2798 			helps.push_back(eh->get_class());
2799 		}
2800 	}
2801 
2802 	p_layout->set_value("ScriptEditor", "open_scripts", scripts);
2803 	p_layout->set_value("ScriptEditor", "open_help", helps);
2804 	p_layout->set_value("ScriptEditor", "split_offset", script_split->get_split_offset());
2805 }
2806 
_help_class_open(const String & p_class)2807 void ScriptEditor::_help_class_open(const String &p_class) {
2808 
2809 	if (p_class == "")
2810 		return;
2811 
2812 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2813 
2814 		EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
2815 
2816 		if (eh && eh->get_class() == p_class) {
2817 
2818 			_go_to_tab(i);
2819 			_update_script_names();
2820 			return;
2821 		}
2822 	}
2823 
2824 	EditorHelp *eh = memnew(EditorHelp);
2825 
2826 	eh->set_name(p_class);
2827 	tab_container->add_child(eh);
2828 	_go_to_tab(tab_container->get_tab_count() - 1);
2829 	eh->go_to_class(p_class, 0);
2830 	eh->connect("go_to_help", this, "_help_class_goto");
2831 	_add_recent_script(p_class);
2832 	_sort_list_on_update = true;
2833 	_update_script_names();
2834 	_save_layout();
2835 }
2836 
_help_class_goto(const String & p_desc)2837 void ScriptEditor::_help_class_goto(const String &p_desc) {
2838 
2839 	String cname = p_desc.get_slice(":", 1);
2840 
2841 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2842 
2843 		EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
2844 
2845 		if (eh && eh->get_class() == cname) {
2846 
2847 			_go_to_tab(i);
2848 			eh->go_to_help(p_desc);
2849 			_update_script_names();
2850 			return;
2851 		}
2852 	}
2853 
2854 	EditorHelp *eh = memnew(EditorHelp);
2855 
2856 	eh->set_name(cname);
2857 	tab_container->add_child(eh);
2858 	_go_to_tab(tab_container->get_tab_count() - 1);
2859 	eh->go_to_help(p_desc);
2860 	eh->connect("go_to_help", this, "_help_class_goto");
2861 	_add_recent_script(eh->get_class());
2862 	_sort_list_on_update = true;
2863 	_update_script_names();
2864 	_save_layout();
2865 }
2866 
_update_selected_editor_menu()2867 void ScriptEditor::_update_selected_editor_menu() {
2868 
2869 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2870 
2871 		bool current = tab_container->get_current_tab() == i;
2872 
2873 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2874 		if (se && se->get_edit_menu()) {
2875 
2876 			if (current)
2877 				se->get_edit_menu()->show();
2878 			else
2879 				se->get_edit_menu()->hide();
2880 		}
2881 	}
2882 
2883 	EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_current_tab_control());
2884 	script_search_menu->get_popup()->clear();
2885 	if (eh) {
2886 
2887 		script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND);
2888 		script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT);
2889 		script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3), HELP_SEARCH_FIND_PREVIOUS);
2890 		script_search_menu->get_popup()->add_separator();
2891 		script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES);
2892 		script_search_menu->show();
2893 	} else {
2894 
2895 		if (tab_container->get_child_count() == 0) {
2896 			script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES);
2897 			script_search_menu->show();
2898 		} else {
2899 			script_search_menu->hide();
2900 		}
2901 	}
2902 }
2903 
_update_history_pos(int p_new_pos)2904 void ScriptEditor::_update_history_pos(int p_new_pos) {
2905 
2906 	Node *n = tab_container->get_current_tab_control();
2907 
2908 	if (Object::cast_to<ScriptEditorBase>(n)) {
2909 
2910 		history.write[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state();
2911 	}
2912 	if (Object::cast_to<EditorHelp>(n)) {
2913 
2914 		history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll();
2915 	}
2916 
2917 	history_pos = p_new_pos;
2918 	tab_container->set_current_tab(history[history_pos].control->get_index());
2919 
2920 	n = history[history_pos].control;
2921 
2922 	if (Object::cast_to<ScriptEditorBase>(n)) {
2923 
2924 		Object::cast_to<ScriptEditorBase>(n)->set_edit_state(history[history_pos].state);
2925 		Object::cast_to<ScriptEditorBase>(n)->ensure_focus();
2926 
2927 		Ref<Script> script = Object::cast_to<ScriptEditorBase>(n)->get_edited_resource();
2928 		if (script != NULL) {
2929 			notify_script_changed(script);
2930 		}
2931 	}
2932 
2933 	if (Object::cast_to<EditorHelp>(n)) {
2934 
2935 		Object::cast_to<EditorHelp>(n)->set_scroll(history[history_pos].state);
2936 		Object::cast_to<EditorHelp>(n)->set_focused();
2937 	}
2938 
2939 	n->set_meta("__editor_pass", ++edit_pass);
2940 	_update_script_names();
2941 	_update_history_arrows();
2942 	_update_selected_editor_menu();
2943 }
2944 
_history_forward()2945 void ScriptEditor::_history_forward() {
2946 
2947 	if (history_pos < history.size() - 1) {
2948 		_update_history_pos(history_pos + 1);
2949 	}
2950 }
2951 
_history_back()2952 void ScriptEditor::_history_back() {
2953 
2954 	if (history_pos > 0) {
2955 		_update_history_pos(history_pos - 1);
2956 	}
2957 }
2958 
get_open_scripts() const2959 Vector<Ref<Script> > ScriptEditor::get_open_scripts() const {
2960 
2961 	Vector<Ref<Script> > out_scripts = Vector<Ref<Script> >();
2962 
2963 	for (int i = 0; i < tab_container->get_child_count(); i++) {
2964 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2965 		if (!se)
2966 			continue;
2967 
2968 		Ref<Script> script = se->get_edited_resource();
2969 		if (script != NULL) {
2970 			out_scripts.push_back(script);
2971 		}
2972 	}
2973 
2974 	return out_scripts;
2975 }
2976 
set_scene_root_script(Ref<Script> p_script)2977 void ScriptEditor::set_scene_root_script(Ref<Script> p_script) {
2978 	// Don't open dominant script if using an external editor.
2979 	const bool use_external_editor =
2980 			EditorSettings::get_singleton()->get("text_editor/external/use_external_editor") ||
2981 			(p_script.is_valid() && p_script->get_language()->overrides_external_editor());
2982 	const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/files/open_dominant_script_on_scene_change");
2983 
2984 	if (open_dominant && !use_external_editor && p_script.is_valid()) {
2985 		edit(p_script);
2986 	}
2987 }
2988 
script_goto_method(Ref<Script> p_script,const String & p_method)2989 bool ScriptEditor::script_goto_method(Ref<Script> p_script, const String &p_method) {
2990 
2991 	int line = p_script->get_member_line(p_method);
2992 
2993 	if (line == -1)
2994 		return false;
2995 
2996 	return edit(p_script, line, 0);
2997 }
2998 
set_live_auto_reload_running_scripts(bool p_enabled)2999 void ScriptEditor::set_live_auto_reload_running_scripts(bool p_enabled) {
3000 
3001 	auto_reload_running_scripts = p_enabled;
3002 }
3003 
_help_search(String p_text)3004 void ScriptEditor::_help_search(String p_text) {
3005 	help_search_dialog->popup_dialog(p_text);
3006 }
3007 
_open_script_request(const String & p_path)3008 void ScriptEditor::_open_script_request(const String &p_path) {
3009 
3010 	Ref<Script> script = ResourceLoader::load(p_path);
3011 	if (script.is_valid()) {
3012 		script_editor->edit(script, false);
3013 		return;
3014 	}
3015 
3016 	Error err;
3017 	Ref<TextFile> text_file = script_editor->_load_text_file(p_path, &err);
3018 	if (text_file.is_valid()) {
3019 		script_editor->edit(text_file, false);
3020 		return;
3021 	}
3022 }
3023 
3024 int ScriptEditor::syntax_highlighters_func_count = 0;
3025 CreateSyntaxHighlighterFunc ScriptEditor::syntax_highlighters_funcs[ScriptEditor::SYNTAX_HIGHLIGHTER_FUNC_MAX];
3026 
register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func)3027 void ScriptEditor::register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func) {
3028 	ERR_FAIL_COND(syntax_highlighters_func_count == SYNTAX_HIGHLIGHTER_FUNC_MAX);
3029 	syntax_highlighters_funcs[syntax_highlighters_func_count++] = p_func;
3030 }
3031 
3032 int ScriptEditor::script_editor_func_count = 0;
3033 CreateScriptEditorFunc ScriptEditor::script_editor_funcs[ScriptEditor::SCRIPT_EDITOR_FUNC_MAX];
3034 
register_create_script_editor_function(CreateScriptEditorFunc p_func)3035 void ScriptEditor::register_create_script_editor_function(CreateScriptEditorFunc p_func) {
3036 
3037 	ERR_FAIL_COND(script_editor_func_count == SCRIPT_EDITOR_FUNC_MAX);
3038 	script_editor_funcs[script_editor_func_count++] = p_func;
3039 }
3040 
_script_changed()3041 void ScriptEditor::_script_changed() {
3042 
3043 	NodeDock::singleton->update_lists();
3044 }
3045 
_on_find_in_files_requested(String text)3046 void ScriptEditor::_on_find_in_files_requested(String text) {
3047 
3048 	find_in_files_dialog->set_search_text(text);
3049 	find_in_files_dialog->popup_centered_minsize();
3050 }
3051 
_on_find_in_files_result_selected(String fpath,int line_number,int begin,int end)3052 void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) {
3053 
3054 	if (ResourceLoader::exists(fpath)) {
3055 		RES res = ResourceLoader::load(fpath);
3056 
3057 		if (fpath.get_extension() == "shader") {
3058 			ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_singleton()->get_editor_data().get_editor("Shader"));
3059 			shader_editor->edit(res.ptr());
3060 			shader_editor->make_visible(true);
3061 			shader_editor->get_shader_editor()->goto_line_selection(line_number - 1, begin, end);
3062 			return;
3063 		} else {
3064 			Ref<Script> script = res;
3065 			if (script.is_valid()) {
3066 				edit(script);
3067 
3068 				ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(_get_current_editor());
3069 				if (ste) {
3070 					ste->goto_line_selection(line_number - 1, begin, end);
3071 				}
3072 				return;
3073 			}
3074 		}
3075 	}
3076 
3077 	// If the file is not a valid resource/script, load it as a text file.
3078 	Error err;
3079 	Ref<TextFile> text_file = _load_text_file(fpath, &err);
3080 	if (text_file.is_valid()) {
3081 		edit(text_file);
3082 
3083 		TextEditor *te = Object::cast_to<TextEditor>(_get_current_editor());
3084 		if (te) {
3085 			te->goto_line_selection(line_number - 1, begin, end);
3086 		}
3087 	}
3088 }
3089 
_start_find_in_files(bool with_replace)3090 void ScriptEditor::_start_find_in_files(bool with_replace) {
3091 
3092 	FindInFiles *f = find_in_files->get_finder();
3093 
3094 	f->set_search_text(find_in_files_dialog->get_search_text());
3095 	f->set_match_case(find_in_files_dialog->is_match_case());
3096 	f->set_whole_words(find_in_files_dialog->is_whole_words());
3097 	f->set_folder(find_in_files_dialog->get_folder());
3098 	f->set_filter(find_in_files_dialog->get_filter());
3099 
3100 	find_in_files->set_with_replace(with_replace);
3101 	find_in_files->start_search();
3102 
3103 	editor->make_bottom_panel_item_visible(find_in_files);
3104 }
3105 
_on_find_in_files_modified_files(PoolStringArray paths)3106 void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) {
3107 
3108 	_test_script_times_on_disk();
3109 	_update_modified_scripts_for_external_editor();
3110 }
3111 
_filter_scripts_text_changed(const String & p_newtext)3112 void ScriptEditor::_filter_scripts_text_changed(const String &p_newtext) {
3113 	_update_script_names();
3114 }
3115 
_filter_methods_text_changed(const String & p_newtext)3116 void ScriptEditor::_filter_methods_text_changed(const String &p_newtext) {
3117 	_update_members_overview();
3118 }
3119 
_bind_methods()3120 void ScriptEditor::_bind_methods() {
3121 
3122 	ClassDB::bind_method("_file_dialog_action", &ScriptEditor::_file_dialog_action);
3123 	ClassDB::bind_method("_tab_changed", &ScriptEditor::_tab_changed);
3124 	ClassDB::bind_method("_menu_option", &ScriptEditor::_menu_option);
3125 	ClassDB::bind_method("_close_current_tab", &ScriptEditor::_close_current_tab);
3126 	ClassDB::bind_method("_close_discard_current_tab", &ScriptEditor::_close_discard_current_tab);
3127 	ClassDB::bind_method("_close_docs_tab", &ScriptEditor::_close_docs_tab);
3128 	ClassDB::bind_method("_close_all_tabs", &ScriptEditor::_close_all_tabs);
3129 	ClassDB::bind_method("_close_other_tabs", &ScriptEditor::_close_other_tabs);
3130 	ClassDB::bind_method("_open_recent_script", &ScriptEditor::_open_recent_script);
3131 	ClassDB::bind_method("_theme_option", &ScriptEditor::_theme_option);
3132 	ClassDB::bind_method("_editor_play", &ScriptEditor::_editor_play);
3133 	ClassDB::bind_method("_editor_pause", &ScriptEditor::_editor_pause);
3134 	ClassDB::bind_method("_editor_stop", &ScriptEditor::_editor_stop);
3135 	ClassDB::bind_method("_add_callback", &ScriptEditor::_add_callback);
3136 	ClassDB::bind_method("_reload_scripts", &ScriptEditor::_reload_scripts);
3137 	ClassDB::bind_method("_resave_scripts", &ScriptEditor::_resave_scripts);
3138 	ClassDB::bind_method("_res_saved_callback", &ScriptEditor::_res_saved_callback);
3139 	ClassDB::bind_method("_goto_script_line", &ScriptEditor::_goto_script_line);
3140 	ClassDB::bind_method("_goto_script_line2", &ScriptEditor::_goto_script_line2);
3141 	ClassDB::bind_method("_set_execution", &ScriptEditor::_set_execution);
3142 	ClassDB::bind_method("_clear_execution", &ScriptEditor::_clear_execution);
3143 	ClassDB::bind_method("_help_search", &ScriptEditor::_help_search);
3144 	ClassDB::bind_method("_save_history", &ScriptEditor::_save_history);
3145 	ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path);
3146 
3147 	ClassDB::bind_method("_breaked", &ScriptEditor::_breaked);
3148 	ClassDB::bind_method("_show_debugger", &ScriptEditor::_show_debugger);
3149 	ClassDB::bind_method("_get_debug_tooltip", &ScriptEditor::_get_debug_tooltip);
3150 	ClassDB::bind_method("_autosave_scripts", &ScriptEditor::_autosave_scripts);
3151 	ClassDB::bind_method("_update_autosave_timer", &ScriptEditor::_update_autosave_timer);
3152 	ClassDB::bind_method("_editor_settings_changed", &ScriptEditor::_editor_settings_changed);
3153 	ClassDB::bind_method("_update_script_names", &ScriptEditor::_update_script_names);
3154 	ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections);
3155 	ClassDB::bind_method("_tree_changed", &ScriptEditor::_tree_changed);
3156 	ClassDB::bind_method("_members_overview_selected", &ScriptEditor::_members_overview_selected);
3157 	ClassDB::bind_method("_help_overview_selected", &ScriptEditor::_help_overview_selected);
3158 	ClassDB::bind_method("_script_selected", &ScriptEditor::_script_selected);
3159 	ClassDB::bind_method("_script_created", &ScriptEditor::_script_created);
3160 	ClassDB::bind_method("_script_split_dragged", &ScriptEditor::_script_split_dragged);
3161 	ClassDB::bind_method("_help_class_open", &ScriptEditor::_help_class_open);
3162 	ClassDB::bind_method("_help_class_goto", &ScriptEditor::_help_class_goto);
3163 	ClassDB::bind_method("_request_help", &ScriptEditor::_help_class_open);
3164 	ClassDB::bind_method("_history_forward", &ScriptEditor::_history_forward);
3165 	ClassDB::bind_method("_history_back", &ScriptEditor::_history_back);
3166 	ClassDB::bind_method("_live_auto_reload_running_scripts", &ScriptEditor::_live_auto_reload_running_scripts);
3167 	ClassDB::bind_method("_unhandled_input", &ScriptEditor::_unhandled_input);
3168 	ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input);
3169 	ClassDB::bind_method("_toggle_members_overview_alpha_sort", &ScriptEditor::_toggle_members_overview_alpha_sort);
3170 	ClassDB::bind_method("_update_members_overview", &ScriptEditor::_update_members_overview);
3171 	ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed);
3172 	ClassDB::bind_method("_filter_scripts_text_changed", &ScriptEditor::_filter_scripts_text_changed);
3173 	ClassDB::bind_method("_filter_methods_text_changed", &ScriptEditor::_filter_methods_text_changed);
3174 	ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts);
3175 	ClassDB::bind_method("_on_find_in_files_requested", &ScriptEditor::_on_find_in_files_requested);
3176 	ClassDB::bind_method("_start_find_in_files", &ScriptEditor::_start_find_in_files);
3177 	ClassDB::bind_method("_on_find_in_files_result_selected", &ScriptEditor::_on_find_in_files_result_selected);
3178 	ClassDB::bind_method("_on_find_in_files_modified_files", &ScriptEditor::_on_find_in_files_modified_files);
3179 
3180 	ClassDB::bind_method(D_METHOD("get_drag_data_fw", "point", "from"), &ScriptEditor::get_drag_data_fw);
3181 	ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw);
3182 	ClassDB::bind_method(D_METHOD("drop_data_fw", "point", "data", "from"), &ScriptEditor::drop_data_fw);
3183 
3184 	ClassDB::bind_method(D_METHOD("goto_line", "line_number"), &ScriptEditor::_goto_script_line2);
3185 	ClassDB::bind_method(D_METHOD("get_current_script"), &ScriptEditor::_get_current_script);
3186 	ClassDB::bind_method(D_METHOD("get_open_scripts"), &ScriptEditor::_get_open_scripts);
3187 	ClassDB::bind_method(D_METHOD("open_script_create_dialog", "base_name", "base_path"), &ScriptEditor::open_script_create_dialog);
3188 
3189 	ADD_SIGNAL(MethodInfo("editor_script_changed", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
3190 	ADD_SIGNAL(MethodInfo("script_close", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
3191 }
3192 
ScriptEditor(EditorNode * p_editor)3193 ScriptEditor::ScriptEditor(EditorNode *p_editor) {
3194 
3195 	current_theme = "";
3196 
3197 	completion_cache = memnew(EditorScriptCodeCompletionCache);
3198 	restoring_layout = false;
3199 	waiting_update_names = false;
3200 	pending_auto_reload = false;
3201 	auto_reload_running_scripts = true;
3202 	members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/script_list/show_members_overview");
3203 	help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index");
3204 	editor = p_editor;
3205 
3206 	VBoxContainer *main_container = memnew(VBoxContainer);
3207 	add_child(main_container);
3208 
3209 	menu_hb = memnew(HBoxContainer);
3210 	main_container->add_child(menu_hb);
3211 
3212 	script_split = memnew(HSplitContainer);
3213 	main_container->add_child(script_split);
3214 	script_split->set_v_size_flags(SIZE_EXPAND_FILL);
3215 
3216 	list_split = memnew(VSplitContainer);
3217 	script_split->add_child(list_split);
3218 	list_split->set_v_size_flags(SIZE_EXPAND_FILL);
3219 
3220 	scripts_vbox = memnew(VBoxContainer);
3221 	scripts_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
3222 	list_split->add_child(scripts_vbox);
3223 
3224 	filter_scripts = memnew(LineEdit);
3225 	filter_scripts->set_placeholder(TTR("Filter scripts"));
3226 	filter_scripts->set_clear_button_enabled(true);
3227 	filter_scripts->connect("text_changed", this, "_filter_scripts_text_changed");
3228 	scripts_vbox->add_child(filter_scripts);
3229 
3230 	script_list = memnew(ItemList);
3231 	scripts_vbox->add_child(script_list);
3232 	script_list->set_custom_minimum_size(Size2(150, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
3233 	script_list->set_v_size_flags(SIZE_EXPAND_FILL);
3234 	script_split->set_split_offset(70 * EDSCALE);
3235 	_sort_list_on_update = true;
3236 	script_list->connect("gui_input", this, "_script_list_gui_input", varray(), CONNECT_DEFERRED);
3237 	script_list->set_allow_rmb_select(true);
3238 	script_list->set_drag_forwarding(this);
3239 
3240 	context_menu = memnew(PopupMenu);
3241 	add_child(context_menu);
3242 	context_menu->connect("id_pressed", this, "_menu_option");
3243 	context_menu->set_hide_on_window_lose_focus(true);
3244 
3245 	overview_vbox = memnew(VBoxContainer);
3246 	overview_vbox->set_custom_minimum_size(Size2(0, 90));
3247 	overview_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
3248 
3249 	list_split->add_child(overview_vbox);
3250 	buttons_hbox = memnew(HBoxContainer);
3251 	overview_vbox->add_child(buttons_hbox);
3252 
3253 	filename = memnew(Label);
3254 	filename->set_clip_text(true);
3255 	filename->set_h_size_flags(SIZE_EXPAND_FILL);
3256 	filename->add_style_override("normal", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal", "LineEdit"));
3257 	buttons_hbox->add_child(filename);
3258 
3259 	members_overview_alphabeta_sort_button = memnew(ToolButton);
3260 	members_overview_alphabeta_sort_button->set_tooltip(TTR("Toggle alphabetical sorting of the method list."));
3261 	members_overview_alphabeta_sort_button->set_toggle_mode(true);
3262 	members_overview_alphabeta_sort_button->set_pressed(EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically"));
3263 	members_overview_alphabeta_sort_button->connect("toggled", this, "_toggle_members_overview_alpha_sort");
3264 
3265 	buttons_hbox->add_child(members_overview_alphabeta_sort_button);
3266 
3267 	filter_methods = memnew(LineEdit);
3268 	filter_methods->set_placeholder(TTR("Filter methods"));
3269 	filter_methods->set_clear_button_enabled(true);
3270 	filter_methods->connect("text_changed", this, "_filter_methods_text_changed");
3271 	overview_vbox->add_child(filter_methods);
3272 
3273 	members_overview = memnew(ItemList);
3274 	overview_vbox->add_child(members_overview);
3275 
3276 	members_overview->set_allow_reselect(true);
3277 	members_overview->set_custom_minimum_size(Size2(0, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
3278 	members_overview->set_v_size_flags(SIZE_EXPAND_FILL);
3279 	members_overview->set_allow_rmb_select(true);
3280 
3281 	help_overview = memnew(ItemList);
3282 	overview_vbox->add_child(help_overview);
3283 	help_overview->set_allow_reselect(true);
3284 	help_overview->set_custom_minimum_size(Size2(0, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
3285 	help_overview->set_v_size_flags(SIZE_EXPAND_FILL);
3286 
3287 	tab_container = memnew(TabContainer);
3288 	tab_container->set_tabs_visible(false);
3289 	tab_container->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
3290 	script_split->add_child(tab_container);
3291 	tab_container->set_h_size_flags(SIZE_EXPAND_FILL);
3292 
3293 	ED_SHORTCUT("script_editor/window_sort", TTR("Sort"));
3294 	ED_SHORTCUT("script_editor/window_move_up", TTR("Move Up"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_UP);
3295 	ED_SHORTCUT("script_editor/window_move_down", TTR("Move Down"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_DOWN);
3296 	ED_SHORTCUT("script_editor/next_script", TTR("Next script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_PERIOD); // these should be KEY_GREATER and KEY_LESS but those don't work
3297 	ED_SHORTCUT("script_editor/prev_script", TTR("Previous script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_COMMA);
3298 	set_process_unhandled_input(true);
3299 
3300 	file_menu = memnew(MenuButton);
3301 	menu_hb->add_child(file_menu);
3302 	file_menu->set_text(TTR("File"));
3303 	file_menu->set_switch_on_hover(true);
3304 	file_menu->get_popup()->set_hide_on_window_lose_focus(true);
3305 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script...")), FILE_NEW);
3306 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New Text File...")), FILE_NEW_TEXTFILE);
3307 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open...")), FILE_OPEN);
3308 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T), FILE_REOPEN_CLOSED);
3309 	file_menu->get_popup()->add_submenu_item(TTR("Open Recent"), "RecentScripts", FILE_OPEN_RECENT);
3310 
3311 	recent_scripts = memnew(PopupMenu);
3312 	recent_scripts->set_name("RecentScripts");
3313 	file_menu->get_popup()->add_child(recent_scripts);
3314 	recent_scripts->connect("id_pressed", this, "_open_recent_script");
3315 	_update_recent_scripts();
3316 
3317 	file_menu->get_popup()->add_separator();
3318 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KEY_MASK_ALT | KEY_MASK_CMD | KEY_S), FILE_SAVE);
3319 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As...")), FILE_SAVE_AS);
3320 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_S), FILE_SAVE_ALL);
3321 	file_menu->get_popup()->add_separator();
3322 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT);
3323 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy_path", TTR("Copy Script Path")), FILE_COPY_PATH);
3324 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show in FileSystem")), SHOW_IN_FILE_SYSTEM);
3325 	file_menu->get_popup()->add_separator();
3326 
3327 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV);
3328 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KEY_MASK_ALT | KEY_RIGHT), WINDOW_NEXT);
3329 	file_menu->get_popup()->add_separator();
3330 
3331 	file_menu->get_popup()->add_submenu_item(TTR("Theme"), "Theme", FILE_THEME);
3332 
3333 	theme_submenu = memnew(PopupMenu);
3334 	theme_submenu->set_name("Theme");
3335 	file_menu->get_popup()->add_child(theme_submenu);
3336 	theme_submenu->connect("id_pressed", this, "_theme_option");
3337 	theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/import_theme", TTR("Import Theme...")), THEME_IMPORT);
3338 	theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/reload_theme", TTR("Reload Theme")), THEME_RELOAD);
3339 
3340 	theme_submenu->add_separator();
3341 	theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme", TTR("Save Theme")), THEME_SAVE);
3342 	theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme_as", TTR("Save Theme As...")), THEME_SAVE_AS);
3343 
3344 	file_menu->get_popup()->add_separator();
3345 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_file", TTR("Close"), KEY_MASK_CMD | KEY_W), FILE_CLOSE);
3346 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_all", TTR("Close All")), CLOSE_ALL);
3347 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_other_tabs", TTR("Close Other Tabs")), CLOSE_OTHER_TABS);
3348 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_docs", TTR("Close Docs")), CLOSE_DOCS);
3349 
3350 	file_menu->get_popup()->add_separator();
3351 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/run_file", TTR("Run"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_X), FILE_RUN);
3352 
3353 	file_menu->get_popup()->add_separator();
3354 	file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/toggle_scripts_panel", TTR("Toggle Scripts Panel"), KEY_MASK_CMD | KEY_BACKSLASH), TOGGLE_SCRIPTS_PANEL);
3355 	file_menu->get_popup()->connect("id_pressed", this, "_menu_option");
3356 
3357 	script_search_menu = memnew(MenuButton);
3358 	menu_hb->add_child(script_search_menu);
3359 	script_search_menu->set_text(TTR("Search"));
3360 	script_search_menu->set_switch_on_hover(true);
3361 	script_search_menu->get_popup()->set_hide_on_window_lose_focus(true);
3362 	script_search_menu->get_popup()->connect("id_pressed", this, "_menu_option");
3363 
3364 	debug_menu = memnew(MenuButton);
3365 	menu_hb->add_child(debug_menu);
3366 	debug_menu->set_text(TTR("Debug"));
3367 	debug_menu->set_switch_on_hover(true);
3368 	debug_menu->get_popup()->set_hide_on_window_lose_focus(true);
3369 	debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP);
3370 	debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT);
3371 	debug_menu->get_popup()->add_separator();
3372 	debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/break", TTR("Break")), DEBUG_BREAK);
3373 	debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/continue", TTR("Continue"), KEY_F12), DEBUG_CONTINUE);
3374 	debug_menu->get_popup()->add_separator();
3375 	//debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW);
3376 	debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")), DEBUG_SHOW_KEEP_OPEN);
3377 	debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor")), DEBUG_WITH_EXTERNAL_EDITOR);
3378 	debug_menu->get_popup()->connect("id_pressed", this, "_menu_option");
3379 
3380 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
3381 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
3382 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true);
3383 	debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
3384 
3385 	menu_hb->add_spacer();
3386 
3387 	script_icon = memnew(TextureRect);
3388 	menu_hb->add_child(script_icon);
3389 	script_name_label = memnew(Label);
3390 	menu_hb->add_child(script_name_label);
3391 
3392 	script_icon->hide();
3393 	script_name_label->hide();
3394 
3395 	menu_hb->add_spacer();
3396 
3397 	site_search = memnew(ToolButton);
3398 	site_search->set_text(TTR("Online Docs"));
3399 	site_search->connect("pressed", this, "_menu_option", varray(SEARCH_WEBSITE));
3400 	menu_hb->add_child(site_search);
3401 	site_search->set_tooltip(TTR("Open Godot online documentation."));
3402 
3403 	help_search = memnew(ToolButton);
3404 	help_search->set_text(TTR("Search Help"));
3405 	help_search->connect("pressed", this, "_menu_option", varray(SEARCH_HELP));
3406 	menu_hb->add_child(help_search);
3407 	help_search->set_tooltip(TTR("Search the reference documentation."));
3408 
3409 	menu_hb->add_child(memnew(VSeparator));
3410 
3411 	script_back = memnew(ToolButton);
3412 	script_back->connect("pressed", this, "_history_back");
3413 	menu_hb->add_child(script_back);
3414 	script_back->set_disabled(true);
3415 	script_back->set_tooltip(TTR("Go to previous edited document."));
3416 
3417 	script_forward = memnew(ToolButton);
3418 	script_forward->connect("pressed", this, "_history_forward");
3419 	menu_hb->add_child(script_forward);
3420 	script_forward->set_disabled(true);
3421 	script_forward->set_tooltip(TTR("Go to next edited document."));
3422 
3423 	tab_container->connect("tab_changed", this, "_tab_changed");
3424 
3425 	erase_tab_confirm = memnew(ConfirmationDialog);
3426 	erase_tab_confirm->get_ok()->set_text(TTR("Save"));
3427 	erase_tab_confirm->add_button(TTR("Discard"), OS::get_singleton()->get_swap_ok_cancel(), "discard");
3428 	erase_tab_confirm->connect("confirmed", this, "_close_current_tab");
3429 	erase_tab_confirm->connect("custom_action", this, "_close_discard_current_tab");
3430 	add_child(erase_tab_confirm);
3431 
3432 	script_create_dialog = memnew(ScriptCreateDialog);
3433 	script_create_dialog->set_title(TTR("Create Script"));
3434 	add_child(script_create_dialog);
3435 	script_create_dialog->connect("script_created", this, "_script_created");
3436 
3437 	file_dialog_option = -1;
3438 	file_dialog = memnew(EditorFileDialog);
3439 	add_child(file_dialog);
3440 	file_dialog->connect("file_selected", this, "_file_dialog_action");
3441 
3442 	error_dialog = memnew(AcceptDialog);
3443 	add_child(error_dialog);
3444 
3445 	debugger = memnew(ScriptEditorDebugger(editor));
3446 	debugger->connect("goto_script_line", this, "_goto_script_line");
3447 	debugger->connect("set_execution", this, "_set_execution");
3448 	debugger->connect("clear_execution", this, "_clear_execution");
3449 	debugger->connect("show_debugger", this, "_show_debugger");
3450 
3451 	disk_changed = memnew(ConfirmationDialog);
3452 	{
3453 		VBoxContainer *vbc = memnew(VBoxContainer);
3454 		disk_changed->add_child(vbc);
3455 
3456 		Label *dl = memnew(Label);
3457 		dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?:"));
3458 		vbc->add_child(dl);
3459 
3460 		disk_changed_list = memnew(Tree);
3461 		vbc->add_child(disk_changed_list);
3462 		disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL);
3463 
3464 		disk_changed->connect("confirmed", this, "_reload_scripts");
3465 		disk_changed->get_ok()->set_text(TTR("Reload"));
3466 
3467 		disk_changed->add_button(TTR("Resave"), !OS::get_singleton()->get_swap_ok_cancel(), "resave");
3468 		disk_changed->connect("custom_action", this, "_resave_scripts");
3469 	}
3470 
3471 	add_child(disk_changed);
3472 
3473 	script_editor = this;
3474 
3475 	Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
3476 	debugger->set_tool_button(db);
3477 
3478 	debugger->connect("breaked", this, "_breaked");
3479 
3480 	autosave_timer = memnew(Timer);
3481 	autosave_timer->set_one_shot(false);
3482 	autosave_timer->connect(SceneStringNames::get_singleton()->tree_entered, this, "_update_autosave_timer");
3483 	autosave_timer->connect("timeout", this, "_autosave_scripts");
3484 	add_child(autosave_timer);
3485 
3486 	grab_focus_block = false;
3487 
3488 	help_search_dialog = memnew(EditorHelpSearch);
3489 	add_child(help_search_dialog);
3490 	help_search_dialog->connect("go_to_help", this, "_help_class_goto");
3491 
3492 	find_in_files_dialog = memnew(FindInFilesDialog);
3493 	find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, this, "_start_find_in_files", varray(false));
3494 	find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true));
3495 	add_child(find_in_files_dialog);
3496 	find_in_files = memnew(FindInFilesPanel);
3497 	find_in_files_button = editor->add_bottom_panel_item(TTR("Search Results"), find_in_files);
3498 	find_in_files->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
3499 	find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected");
3500 	find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files");
3501 	find_in_files->hide();
3502 	find_in_files_button->hide();
3503 
3504 	history_pos = -1;
3505 	//debugger_gui->hide();
3506 
3507 	edit_pass = 0;
3508 	trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");
3509 	convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/indent/convert_indent_on_save");
3510 	use_space_indentation = EditorSettings::get_singleton()->get("text_editor/indent/type");
3511 
3512 	ScriptServer::edit_request_func = _open_script_request;
3513 
3514 	add_style_override("panel", editor->get_gui_base()->get_stylebox("ScriptEditorPanel", "EditorStyles"));
3515 	tab_container->add_style_override("panel", editor->get_gui_base()->get_stylebox("ScriptEditor", "EditorStyles"));
3516 }
3517 
~ScriptEditor()3518 ScriptEditor::~ScriptEditor() {
3519 
3520 	memdelete(completion_cache);
3521 }
3522 
edit(Object * p_object)3523 void ScriptEditorPlugin::edit(Object *p_object) {
3524 
3525 	if (Object::cast_to<Script>(p_object)) {
3526 
3527 		Script *p_script = Object::cast_to<Script>(p_object);
3528 		String res_path = p_script->get_path().get_slice("::", 0);
3529 
3530 		if (_is_built_in_script(p_script)) {
3531 			if (ResourceLoader::get_resource_type(res_path) == "PackedScene") {
3532 				if (!EditorNode::get_singleton()->is_scene_open(res_path)) {
3533 					EditorNode::get_singleton()->load_scene(res_path);
3534 				}
3535 			} else {
3536 				EditorNode::get_singleton()->load_resource(res_path);
3537 			}
3538 		}
3539 		script_editor->edit(p_script);
3540 	} else if (Object::cast_to<TextFile>(p_object)) {
3541 		script_editor->edit(Object::cast_to<TextFile>(p_object));
3542 	}
3543 }
3544 
handles(Object * p_object) const3545 bool ScriptEditorPlugin::handles(Object *p_object) const {
3546 
3547 	if (Object::cast_to<TextFile>(p_object)) {
3548 		return true;
3549 	}
3550 
3551 	if (Object::cast_to<Script>(p_object)) {
3552 		return true;
3553 	}
3554 
3555 	return p_object->is_class("Script");
3556 }
3557 
make_visible(bool p_visible)3558 void ScriptEditorPlugin::make_visible(bool p_visible) {
3559 
3560 	if (p_visible) {
3561 		script_editor->show();
3562 		script_editor->set_process(true);
3563 		script_editor->ensure_select_current();
3564 	} else {
3565 
3566 		script_editor->hide();
3567 		script_editor->set_process(false);
3568 	}
3569 }
3570 
selected_notify()3571 void ScriptEditorPlugin::selected_notify() {
3572 
3573 	script_editor->ensure_select_current();
3574 }
3575 
save_external_data()3576 void ScriptEditorPlugin::save_external_data() {
3577 
3578 	script_editor->save_all_scripts();
3579 }
3580 
apply_changes()3581 void ScriptEditorPlugin::apply_changes() {
3582 
3583 	script_editor->apply_scripts();
3584 }
3585 
restore_global_state()3586 void ScriptEditorPlugin::restore_global_state() {
3587 }
3588 
save_global_state()3589 void ScriptEditorPlugin::save_global_state() {
3590 }
3591 
set_window_layout(Ref<ConfigFile> p_layout)3592 void ScriptEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
3593 
3594 	script_editor->set_window_layout(p_layout);
3595 }
3596 
get_window_layout(Ref<ConfigFile> p_layout)3597 void ScriptEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
3598 
3599 	script_editor->get_window_layout(p_layout);
3600 }
3601 
get_breakpoints(List<String> * p_breakpoints)3602 void ScriptEditorPlugin::get_breakpoints(List<String> *p_breakpoints) {
3603 
3604 	script_editor->get_breakpoints(p_breakpoints);
3605 }
3606 
edited_scene_changed()3607 void ScriptEditorPlugin::edited_scene_changed() {
3608 
3609 	script_editor->edited_scene_changed();
3610 }
3611 
ScriptEditorPlugin(EditorNode * p_node)3612 ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
3613 
3614 	editor = p_node;
3615 	script_editor = memnew(ScriptEditor(p_node));
3616 	editor->get_viewport()->add_child(script_editor);
3617 	script_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
3618 
3619 	script_editor->hide();
3620 
3621 	EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change", true);
3622 	ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/files/auto_reload_and_parse_scripts_on_save", true));
3623 	EDITOR_DEF("text_editor/files/open_dominant_script_on_scene_change", true);
3624 	EDITOR_DEF("text_editor/external/use_external_editor", false);
3625 	EDITOR_DEF("text_editor/external/exec_path", "");
3626 	EDITOR_DEF("text_editor/script_list/script_temperature_enabled", true);
3627 	EDITOR_DEF("text_editor/script_list/highlight_current_script", true);
3628 	EDITOR_DEF("text_editor/script_list/script_temperature_history_size", 15);
3629 	EDITOR_DEF("text_editor/script_list/current_script_background_color", Color(1, 1, 1, 0.3));
3630 	EDITOR_DEF("text_editor/script_list/group_help_pages", true);
3631 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/script_list/sort_scripts_by", PROPERTY_HINT_ENUM, "Name,Path,None"));
3632 	EDITOR_DEF("text_editor/script_list/sort_scripts_by", 0);
3633 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/script_list/list_script_names_as", PROPERTY_HINT_ENUM, "Name,Parent Directory And Name,Full Path"));
3634 	EDITOR_DEF("text_editor/script_list/list_script_names_as", 0);
3635 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_path", PROPERTY_HINT_GLOBAL_FILE));
3636 	EDITOR_DEF("text_editor/external/exec_flags", "{file}");
3637 	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_flags", PROPERTY_HINT_PLACEHOLDER_TEXT, "Call flags with placeholders: {project}, {file}, {col}, {line}."));
3638 
3639 	ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T);
3640 	ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Scripts"));
3641 }
3642 
~ScriptEditorPlugin()3643 ScriptEditorPlugin::~ScriptEditorPlugin() {
3644 }
3645