1/**************************************************************************/
2/* script_editor_plugin.h */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#ifndef SCRIPT_EDITOR_PLUGIN_H
32#define SCRIPT_EDITOR_PLUGIN_H
33
34#include "core/object/script_language.h"
35#include "editor/editor_plugin.h"
36#include "scene/gui/dialogs.h"
37#include "scene/gui/panel_container.h"
38#include "scene/resources/syntax_highlighter.h"
39#include "scene/resources/text_file.h"
40
41class EditorFileDialog;
42class EditorHelpSearch;
43class FindReplaceBar;
44class HSplitContainer;
45class ItemList;
46class MenuButton;
47class TabContainer;
48class TextureRect;
49class Tree;
50class VSplitContainer;
51class WindowWrapper;
52
53class EditorSyntaxHighlighter : public SyntaxHighlighter {
54 GDCLASS(EditorSyntaxHighlighter, SyntaxHighlighter)
55
56private:
57 Ref<RefCounted> edited_resourse;
58
59protected:
60 static void _bind_methods();
61
62 GDVIRTUAL0RC(String, _get_name)
63 GDVIRTUAL0RC(PackedStringArray, _get_supported_languages)
64
65public:
66 virtual String _get_name() const;
67 virtual PackedStringArray _get_supported_languages() const;
68
69 void _set_edited_resource(const Ref<Resource> &p_res) { edited_resourse = p_res; }
70 Ref<RefCounted> _get_edited_resource() { return edited_resourse; }
71
72 virtual Ref<EditorSyntaxHighlighter> _create() const;
73};
74
75class EditorStandardSyntaxHighlighter : public EditorSyntaxHighlighter {
76 GDCLASS(EditorStandardSyntaxHighlighter, EditorSyntaxHighlighter)
77
78private:
79 Ref<CodeHighlighter> highlighter;
80
81public:
82 virtual void _update_cache() override;
83 virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override { return highlighter->get_line_syntax_highlighting(p_line); }
84
85 virtual String _get_name() const override { return TTR("Standard"); }
86
87 virtual Ref<EditorSyntaxHighlighter> _create() const override;
88
89 EditorStandardSyntaxHighlighter() { highlighter.instantiate(); }
90};
91
92class EditorPlainTextSyntaxHighlighter : public EditorSyntaxHighlighter {
93 GDCLASS(EditorPlainTextSyntaxHighlighter, EditorSyntaxHighlighter)
94
95public:
96 virtual String _get_name() const override { return TTR("Plain Text"); }
97
98 virtual Ref<EditorSyntaxHighlighter> _create() const override;
99};
100
101class EditorJSONSyntaxHighlighter : public EditorSyntaxHighlighter {
102 GDCLASS(EditorJSONSyntaxHighlighter, EditorSyntaxHighlighter)
103
104private:
105 Ref<CodeHighlighter> highlighter;
106
107public:
108 virtual void _update_cache() override;
109 virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override { return highlighter->get_line_syntax_highlighting(p_line); }
110
111 virtual PackedStringArray _get_supported_languages() const override { return PackedStringArray{ "json" }; }
112 virtual String _get_name() const override { return TTR("JSON"); }
113
114 virtual Ref<EditorSyntaxHighlighter> _create() const override;
115
116 EditorJSONSyntaxHighlighter() { highlighter.instantiate(); }
117};
118
119///////////////////////////////////////////////////////////////////////////////
120
121class ScriptEditorQuickOpen : public ConfirmationDialog {
122 GDCLASS(ScriptEditorQuickOpen, ConfirmationDialog);
123
124 LineEdit *search_box = nullptr;
125 Tree *search_options = nullptr;
126 String function;
127
128 void _update_search();
129
130 void _sbox_input(const Ref<InputEvent> &p_ie);
131 Vector<String> functions;
132
133 void _confirmed();
134 void _text_changed(const String &p_newtext);
135
136protected:
137 void _notification(int p_what);
138 static void _bind_methods();
139
140public:
141 void popup_dialog(const Vector<String> &p_functions, bool p_dontclear = false);
142 ScriptEditorQuickOpen();
143};
144
145class EditorDebuggerNode;
146
147class ScriptEditorBase : public VBoxContainer {
148 GDCLASS(ScriptEditorBase, VBoxContainer);
149
150protected:
151 static void _bind_methods();
152
153public:
154 virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) = 0;
155 virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) = 0;
156
157 virtual void apply_code() = 0;
158 virtual Ref<Resource> get_edited_resource() const = 0;
159 virtual Vector<String> get_functions() = 0;
160 virtual void set_edited_resource(const Ref<Resource> &p_res) = 0;
161 virtual void enable_editor(Control *p_shortcut_context = nullptr) = 0;
162 virtual void reload_text() = 0;
163 virtual String get_name() = 0;
164 virtual Ref<Texture2D> get_theme_icon() = 0;
165 virtual bool is_unsaved() = 0;
166 virtual Variant get_edit_state() = 0;
167 virtual void set_edit_state(const Variant &p_state) = 0;
168 virtual Variant get_navigation_state() = 0;
169 virtual void goto_line(int p_line, bool p_with_error = false) = 0;
170 virtual void set_executing_line(int p_line) = 0;
171 virtual void clear_executing_line() = 0;
172 virtual void trim_trailing_whitespace() = 0;
173 virtual void insert_final_newline() = 0;
174 virtual void convert_indent() = 0;
175 virtual void ensure_focus() = 0;
176 virtual void tag_saved_version() = 0;
177 virtual void reload(bool p_soft) {}
178 virtual PackedInt32Array get_breakpoints() = 0;
179 virtual void set_breakpoint(int p_line, bool p_enabled) = 0;
180 virtual void clear_breakpoints() = 0;
181 virtual void add_callback(const String &p_function, PackedStringArray p_args) = 0;
182 virtual void update_settings() = 0;
183 virtual void set_debugger_active(bool p_active) = 0;
184 virtual bool can_lose_focus_on_node_selection() { return true; }
185 virtual void update_toggle_scripts_button() {}
186
187 virtual bool show_members_overview() = 0;
188
189 virtual void set_tooltip_request_func(const Callable &p_toolip_callback) = 0;
190 virtual Control *get_edit_menu() = 0;
191 virtual void clear_edit_menu() = 0;
192 virtual void set_find_replace_bar(FindReplaceBar *p_bar) = 0;
193
194 virtual Control *get_base_editor() const = 0;
195
196 virtual void validate() = 0;
197
198 ScriptEditorBase() {}
199};
200
201typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Resource> &p_resource);
202
203class EditorScriptCodeCompletionCache;
204class FindInFilesDialog;
205class FindInFilesPanel;
206
207class ScriptEditor : public PanelContainer {
208 GDCLASS(ScriptEditor, PanelContainer);
209
210 enum {
211 FILE_NEW,
212 FILE_NEW_TEXTFILE,
213 FILE_OPEN,
214 FILE_REOPEN_CLOSED,
215 FILE_OPEN_RECENT,
216 FILE_SAVE,
217 FILE_SAVE_AS,
218 FILE_SAVE_ALL,
219 FILE_THEME,
220 FILE_RUN,
221 FILE_CLOSE,
222 CLOSE_DOCS,
223 CLOSE_ALL,
224 CLOSE_OTHER_TABS,
225 TOGGLE_SCRIPTS_PANEL,
226 SHOW_IN_FILE_SYSTEM,
227 FILE_COPY_PATH,
228 FILE_TOOL_RELOAD_SOFT,
229 SEARCH_IN_FILES,
230 REPLACE_IN_FILES,
231 SEARCH_HELP,
232 SEARCH_WEBSITE,
233 HELP_SEARCH_FIND,
234 HELP_SEARCH_FIND_NEXT,
235 HELP_SEARCH_FIND_PREVIOUS,
236 WINDOW_MOVE_UP,
237 WINDOW_MOVE_DOWN,
238 WINDOW_NEXT,
239 WINDOW_PREV,
240 WINDOW_SORT,
241 WINDOW_SELECT_BASE = 100,
242 };
243
244 enum {
245 THEME_IMPORT,
246 THEME_RELOAD,
247 THEME_SAVE,
248 THEME_SAVE_AS
249 };
250
251 enum ScriptSortBy {
252 SORT_BY_NAME,
253 SORT_BY_PATH,
254 SORT_BY_NONE
255 };
256
257 enum ScriptListName {
258 DISPLAY_NAME,
259 DISPLAY_DIR_AND_NAME,
260 DISPLAY_FULL_PATH,
261 };
262
263 HBoxContainer *menu_hb = nullptr;
264 MenuButton *file_menu = nullptr;
265 MenuButton *edit_menu = nullptr;
266 MenuButton *script_search_menu = nullptr;
267 MenuButton *debug_menu = nullptr;
268 PopupMenu *context_menu = nullptr;
269 Timer *autosave_timer = nullptr;
270 uint64_t idle = 0;
271
272 PopupMenu *recent_scripts = nullptr;
273 PopupMenu *theme_submenu = nullptr;
274
275 Button *help_search = nullptr;
276 Button *site_search = nullptr;
277 Button *make_floating = nullptr;
278 bool is_floating = false;
279 EditorHelpSearch *help_search_dialog = nullptr;
280
281 ItemList *script_list = nullptr;
282 HSplitContainer *script_split = nullptr;
283 ItemList *members_overview = nullptr;
284 LineEdit *filter_scripts = nullptr;
285 LineEdit *filter_methods = nullptr;
286 VBoxContainer *scripts_vbox = nullptr;
287 VBoxContainer *overview_vbox = nullptr;
288 HBoxContainer *buttons_hbox = nullptr;
289 Label *filename = nullptr;
290 Button *members_overview_alphabeta_sort_button = nullptr;
291 bool members_overview_enabled;
292 ItemList *help_overview = nullptr;
293 bool help_overview_enabled;
294 VSplitContainer *list_split = nullptr;
295 TabContainer *tab_container = nullptr;
296 EditorFileDialog *file_dialog = nullptr;
297 AcceptDialog *error_dialog = nullptr;
298 ConfirmationDialog *erase_tab_confirm = nullptr;
299 ScriptCreateDialog *script_create_dialog = nullptr;
300 Button *scripts_visible = nullptr;
301 FindReplaceBar *find_replace_bar = nullptr;
302
303 String current_theme;
304
305 TextureRect *script_icon = nullptr;
306 Label *script_name_label = nullptr;
307
308 Button *script_back = nullptr;
309 Button *script_forward = nullptr;
310
311 FindInFilesDialog *find_in_files_dialog = nullptr;
312 FindInFilesPanel *find_in_files = nullptr;
313 Button *find_in_files_button = nullptr;
314
315 WindowWrapper *window_wrapper = nullptr;
316
317 enum {
318 SCRIPT_EDITOR_FUNC_MAX = 32,
319 };
320
321 static int script_editor_func_count;
322 static CreateScriptEditorFunc script_editor_funcs[SCRIPT_EDITOR_FUNC_MAX];
323
324 Vector<Ref<EditorSyntaxHighlighter>> syntax_highlighters;
325
326 struct ScriptHistory {
327 Control *control = nullptr;
328 Variant state;
329 };
330
331 Vector<ScriptHistory> history;
332 int history_pos;
333
334 List<String> previous_scripts;
335 List<int> script_close_queue;
336
337 void _tab_changed(int p_which);
338 void _menu_option(int p_option);
339 void _theme_option(int p_option);
340 void _show_save_theme_as_dialog();
341 bool _has_docs_tab() const;
342 bool _has_script_tab() const;
343 void _prepare_file_menu();
344 void _file_menu_closed();
345
346 Tree *disk_changed_list = nullptr;
347 ConfirmationDialog *disk_changed = nullptr;
348
349 bool restoring_layout;
350
351 String _get_debug_tooltip(const String &p_text, Node *_se);
352
353 void _resave_scripts(const String &p_str);
354
355 bool _test_script_times_on_disk(Ref<Resource> p_for_script = Ref<Resource>());
356
357 void _add_recent_script(String p_path);
358 void _update_recent_scripts();
359 void _open_recent_script(int p_idx);
360
361 void _show_error_dialog(String p_path);
362
363 void _close_tab(int p_idx, bool p_save = true, bool p_history_back = true);
364 void _update_find_replace_bar();
365
366 void _close_current_tab(bool p_save = true);
367 void _close_discard_current_tab(const String &p_str);
368 void _close_docs_tab();
369 void _close_other_tabs();
370 void _close_all_tabs();
371 void _queue_close_tabs();
372
373 void _copy_script_path();
374
375 void _ask_close_current_unsaved_tab(ScriptEditorBase *current);
376
377 bool grab_focus_block;
378
379 bool pending_auto_reload;
380 bool auto_reload_running_scripts;
381 void _trigger_live_script_reload();
382 void _live_auto_reload_running_scripts();
383
384 void _update_selected_editor_menu();
385
386 EditorScriptCodeCompletionCache *completion_cache = nullptr;
387
388 void _editor_stop();
389
390 int edit_pass;
391
392 void _add_callback(Object *p_obj, const String &p_function, const PackedStringArray &p_args);
393 void _res_saved_callback(const Ref<Resource> &p_res);
394 void _scene_saved_callback(const String &p_path);
395
396 bool open_textfile_after_create = true;
397 bool trim_trailing_whitespace_on_save;
398 bool convert_indent_on_save;
399
400 void _goto_script_line2(int p_line);
401 void _goto_script_line(Ref<RefCounted> p_script, int p_line);
402 void _set_execution(Ref<RefCounted> p_script, int p_line);
403 void _clear_execution(Ref<RefCounted> p_script);
404 void _breaked(bool p_breaked, bool p_can_debug);
405 void _script_created(Ref<Script> p_script);
406 void _set_breakpoint(Ref<RefCounted> p_scrpt, int p_line, bool p_enabled);
407 void _clear_breakpoints();
408 Array _get_cached_breakpoints_for_script(const String &p_path) const;
409
410 ScriptEditorBase *_get_current_editor() const;
411 TypedArray<ScriptEditorBase> _get_open_script_editors() const;
412
413 Ref<ConfigFile> script_editor_cache;
414 void _save_editor_state(ScriptEditorBase *p_editor);
415 void _save_layout();
416 void _editor_settings_changed();
417 void _filesystem_changed();
418 void _files_moved(const String &p_old_file, const String &p_new_file);
419 void _file_removed(const String &p_file);
420 void _autosave_scripts();
421 void _update_autosave_timer();
422
423 void _update_members_overview_visibility();
424 void _update_members_overview();
425 void _toggle_members_overview_alpha_sort(bool p_alphabetic_sort);
426 void _filter_scripts_text_changed(const String &p_newtext);
427 void _filter_methods_text_changed(const String &p_newtext);
428 void _update_script_names();
429 bool _sort_list_on_update;
430
431 void _members_overview_selected(int p_idx);
432 void _script_selected(int p_idx);
433
434 void _update_help_overview_visibility();
435 void _update_help_overview();
436 void _help_overview_selected(int p_idx);
437
438 void _find_scripts(Node *p_base, Node *p_current, HashSet<Ref<Script>> &used);
439
440 void _tree_changed();
441
442 void _split_dragged(float);
443
444 Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
445 bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
446 void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
447
448 virtual void input(const Ref<InputEvent> &p_event) override;
449 virtual void shortcut_input(const Ref<InputEvent> &p_event) override;
450
451 void _script_list_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index);
452 void _make_script_list_context_menu();
453
454 void _help_search(String p_text);
455
456 void _history_forward();
457 void _history_back();
458
459 bool waiting_update_names;
460
461 void _help_class_open(const String &p_class);
462 void _help_class_goto(const String &p_desc);
463 bool _help_tab_goto(const String &p_name, const String &p_desc);
464 void _update_history_arrows();
465 void _save_history();
466 void _go_to_tab(int p_idx);
467 void _update_history_pos(int p_new_pos);
468 void _update_script_colors();
469 void _update_modified_scripts_for_external_editor(Ref<Script> p_for_script = Ref<Script>());
470
471 void _script_changed();
472 int file_dialog_option;
473 void _file_dialog_action(String p_file);
474
475 Ref<Script> _get_current_script();
476 TypedArray<Script> _get_open_scripts() const;
477
478 HashSet<String> textfile_extensions;
479 Ref<TextFile> _load_text_file(const String &p_path, Error *r_error) const;
480 Error _save_text_file(Ref<TextFile> p_text_file, const String &p_path);
481
482 void _on_find_in_files_requested(String text);
483 void _on_replace_in_files_requested(String text);
484 void _on_find_in_files_result_selected(String fpath, int line_number, int begin, int end);
485 void _start_find_in_files(bool with_replace);
486 void _on_find_in_files_modified_files(PackedStringArray paths);
487
488 void _window_changed(bool p_visible);
489
490 static void _open_script_request(const String &p_path);
491 void _close_builtin_scripts_from_scene(const String &p_scene);
492
493 static ScriptEditor *script_editor;
494
495protected:
496 void _notification(int p_what);
497 static void _bind_methods();
498
499public:
500 static ScriptEditor *get_singleton() { return script_editor; }
501
502 bool toggle_scripts_panel();
503 bool is_scripts_panel_toggled();
504 void apply_scripts() const;
505 void reload_scripts(bool p_refresh_only = false);
506 void open_script_create_dialog(const String &p_base_name, const String &p_base_path);
507 void open_text_file_create_dialog(const String &p_base_path, const String &p_base_name = "");
508 Ref<Resource> open_file(const String &p_file);
509
510 void ensure_select_current();
511
512 bool is_editor_floating();
513
514 _FORCE_INLINE_ bool edit(const Ref<Resource> &p_resource, bool p_grab_focus = true) { return edit(p_resource, -1, 0, p_grab_focus); }
515 bool edit(const Ref<Resource> &p_resource, int p_line, int p_col, bool p_grab_focus = true);
516
517 void get_breakpoints(List<String> *p_breakpoints);
518
519 PackedStringArray get_unsaved_scripts() const;
520 void save_current_script();
521 void save_all_scripts();
522
523 void set_window_layout(Ref<ConfigFile> p_layout);
524 void get_window_layout(Ref<ConfigFile> p_layout);
525
526 void set_scene_root_script(Ref<Script> p_script);
527 Vector<Ref<Script>> get_open_scripts() const;
528
529 bool script_goto_method(Ref<Script> p_script, const String &p_method);
530
531 virtual void edited_scene_changed();
532
533 void notify_script_close(const Ref<Script> &p_script);
534 void notify_script_changed(const Ref<Script> &p_script);
535
536 void goto_help(const String &p_desc) { _help_class_goto(p_desc); }
537 void update_doc(const String &p_name);
538 void clear_docs_from_script(const Ref<Script> &p_script);
539 void update_docs_from_script(const Ref<Script> &p_script);
540
541 bool can_take_away_focus() const;
542
543 VSplitContainer *get_left_list_split() { return list_split; }
544
545 void set_live_auto_reload_running_scripts(bool p_enabled);
546
547 void register_syntax_highlighter(const Ref<EditorSyntaxHighlighter> &p_syntax_highlighter);
548 void unregister_syntax_highlighter(const Ref<EditorSyntaxHighlighter> &p_syntax_highlighter);
549
550 static void register_create_script_editor_function(CreateScriptEditorFunc p_func);
551
552 ScriptEditor(WindowWrapper *p_wrapper);
553 ~ScriptEditor();
554};
555
556class ScriptEditorPlugin : public EditorPlugin {
557 GDCLASS(ScriptEditorPlugin, EditorPlugin);
558
559 ScriptEditor *script_editor = nullptr;
560 WindowWrapper *window_wrapper = nullptr;
561
562 String last_editor;
563
564 void _focus_another_editor();
565
566 void _save_last_editor(String p_editor);
567 void _window_visibility_changed(bool p_visible);
568
569protected:
570 void _notification(int p_what);
571
572public:
573 virtual String get_name() const override { return "Script"; }
574 bool has_main_screen() const override { return true; }
575 virtual void edit(Object *p_object) override;
576 virtual bool handles(Object *p_object) const override;
577 virtual void make_visible(bool p_visible) override;
578 virtual void selected_notify() override;
579
580 virtual String get_unsaved_status(const String &p_for_scene) const override;
581 virtual void save_external_data() override;
582 virtual void apply_changes() override;
583
584 virtual void set_window_layout(Ref<ConfigFile> p_layout) override;
585 virtual void get_window_layout(Ref<ConfigFile> p_layout) override;
586
587 virtual void get_breakpoints(List<String> *p_breakpoints) override;
588
589 virtual void edited_scene_changed() override;
590
591 ScriptEditorPlugin();
592 ~ScriptEditorPlugin();
593};
594
595#endif // SCRIPT_EDITOR_PLUGIN_H
596