├── README ├── .gdignore ├── icon.png ├── logo.png ├── readme_preview.png └── changes_hint_toc.png ├── addons └── text_editor │ ├── ext │ ├── ext_txt.gd │ ├── ext_csv.gd │ ├── ext_cfg.gd │ ├── ext_ini.gd │ ├── ext_rpy.gd │ ├── ext_json.gd │ ├── TE_ExtensionHelper.gd │ ├── ext_yaml.gd │ └── ext_md.gd │ ├── te_empty_style.tres │ ├── icons │ ├── icon_red.png │ ├── icon_blue.png │ ├── icon_green.png │ ├── icon_yellow.png │ ├── icon_red.png.import │ ├── icon_blue.png.import │ ├── icon_green.png.import │ └── icon_yellow.png.import │ ├── fonts │ ├── RobotoMono-Bold.ttf │ ├── unifont-13.0.01.ttf │ ├── RobotoMono-Italic.ttf │ ├── RobotoMono-Regular.ttf │ ├── RobotoMono-BoldItalic.ttf │ ├── unifont_upper-13.0.01.ttf │ ├── font.tres │ ├── font_b.tres │ ├── font_i.tres │ ├── font_bi.tres │ └── font_r.tres │ ├── plugin.cfg │ ├── TE_Console.gd │ ├── TE_MetaInfo.gd │ ├── plugin.gd │ ├── TE_LineEdit.gd │ ├── TE_TabScroller.gd │ ├── TE_DragLabel.gd │ ├── TE_MetaTabs.gd │ ├── TE_TagsPanel.gd │ ├── TE_FileTabs.gd │ ├── TE_FileInfoLabel.gd │ ├── TE_RichTextLabel.gd │ ├── TE_SymbolsList.gd │ ├── TE_Search.gd │ ├── TE_ScriptInfo.gd │ ├── TE_Util.gd │ ├── TE_FilesList.gd │ ├── TE_StopWords.gd │ ├── TE_FileEditor.gd │ ├── TextEditor.tscn │ └── TE_Editor.gd ├── default_bus_layout.tres ├── .gitignore ├── LICENSE ├── README.md └── CHANGES.md /README/.gdignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/text_editor/ext/ext_txt.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TE_ExtensionHelper 3 | -------------------------------------------------------------------------------- /addons/text_editor/ext/ext_csv.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TE_ExtensionHelper 3 | 4 | -------------------------------------------------------------------------------- /README/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/README/icon.png -------------------------------------------------------------------------------- /README/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/README/logo.png -------------------------------------------------------------------------------- /default_bus_layout.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="AudioBusLayout" format=2] 2 | 3 | [resource] 4 | -------------------------------------------------------------------------------- /addons/text_editor/ext/ext_cfg.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends "res://addons/text_editor/ext/ext_ini.gd" 3 | -------------------------------------------------------------------------------- /addons/text_editor/te_empty_style.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="StyleBoxEmpty" format=2] 2 | 3 | [resource] 4 | -------------------------------------------------------------------------------- /README/readme_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/README/readme_preview.png -------------------------------------------------------------------------------- /README/changes_hint_toc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/README/changes_hint_toc.png -------------------------------------------------------------------------------- /addons/text_editor/icons/icon_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/icons/icon_red.png -------------------------------------------------------------------------------- /addons/text_editor/icons/icon_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/icons/icon_blue.png -------------------------------------------------------------------------------- /addons/text_editor/icons/icon_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/icons/icon_green.png -------------------------------------------------------------------------------- /addons/text_editor/icons/icon_yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/icons/icon_yellow.png -------------------------------------------------------------------------------- /addons/text_editor/fonts/RobotoMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/fonts/RobotoMono-Bold.ttf -------------------------------------------------------------------------------- /addons/text_editor/fonts/unifont-13.0.01.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/fonts/unifont-13.0.01.ttf -------------------------------------------------------------------------------- /addons/text_editor/fonts/RobotoMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/fonts/RobotoMono-Italic.ttf -------------------------------------------------------------------------------- /addons/text_editor/fonts/RobotoMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/fonts/RobotoMono-Regular.ttf -------------------------------------------------------------------------------- /addons/text_editor/fonts/RobotoMono-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/fonts/RobotoMono-BoldItalic.ttf -------------------------------------------------------------------------------- /addons/text_editor/fonts/unifont_upper-13.0.01.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teebarjunk/Godot-TextEditor/HEAD/addons/text_editor/fonts/unifont_upper-13.0.01.ttf -------------------------------------------------------------------------------- /addons/text_editor/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="TextEditor" 4 | description="A text editor for Godot." 5 | author="teebar" 6 | version="1.12" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/text_editor/TE_Console.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends "res://addons/text_editor/TE_RichTextLabel.gd" 3 | 4 | func _ready(): 5 | clear() 6 | 7 | func msg(msg): 8 | append_bbcode(str(msg)) 9 | newline() 10 | 11 | func err(err): 12 | append_bbcode(clr(err, Color.tomato)) 13 | newline() 14 | 15 | func info(info): 16 | append_bbcode(clr(info, Color.aquamarine)) 17 | newline() 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot-specific ignores 2 | .import/ 3 | export.cfg 4 | export_presets.cfg 5 | # Imported translations (automatically generated from CSV files) 6 | *.translation 7 | 8 | # Mono-specific ignores 9 | .mono/ 10 | data_*/ 11 | 12 | project.godot 13 | 14 | # Text editor related 15 | test_files/ 16 | .trash/ 17 | .trash.json 18 | .text_editor_state.json 19 | word_skip_list.txt 20 | -------------------------------------------------------------------------------- /addons/text_editor/fonts/font.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=4 format=2] 2 | 3 | [ext_resource path="res://addons/text_editor/fonts/RobotoMono-Regular.ttf" type="DynamicFontData" id=1] 4 | [ext_resource path="res://addons/text_editor/fonts/unifont_upper-13.0.01.ttf" type="DynamicFontData" id=2] 5 | [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] 6 | 7 | [resource] 8 | size = 12 9 | use_filter = true 10 | font_data = ExtResource( 1 ) 11 | fallback/0 = ExtResource( 3 ) 12 | fallback/1 = ExtResource( 2 ) 13 | -------------------------------------------------------------------------------- /addons/text_editor/fonts/font_b.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=4 format=2] 2 | 3 | [ext_resource path="res://addons/text_editor/fonts/RobotoMono-Bold.ttf" type="DynamicFontData" id=1] 4 | [ext_resource path="res://addons/text_editor/fonts/unifont_upper-13.0.01.ttf" type="DynamicFontData" id=2] 5 | [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] 6 | 7 | [resource] 8 | size = 12 9 | use_filter = true 10 | font_data = ExtResource( 1 ) 11 | fallback/0 = ExtResource( 3 ) 12 | fallback/1 = ExtResource( 2 ) 13 | -------------------------------------------------------------------------------- /addons/text_editor/fonts/font_i.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=4 format=2] 2 | 3 | [ext_resource path="res://addons/text_editor/fonts/RobotoMono-Italic.ttf" type="DynamicFontData" id=1] 4 | [ext_resource path="res://addons/text_editor/fonts/unifont_upper-13.0.01.ttf" type="DynamicFontData" id=2] 5 | [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] 6 | 7 | [resource] 8 | size = 12 9 | use_filter = true 10 | font_data = ExtResource( 1 ) 11 | fallback/0 = ExtResource( 3 ) 12 | fallback/1 = ExtResource( 2 ) 13 | -------------------------------------------------------------------------------- /addons/text_editor/fonts/font_bi.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=4 format=2] 2 | 3 | [ext_resource path="res://addons/text_editor/fonts/RobotoMono-BoldItalic.ttf" type="DynamicFontData" id=1] 4 | [ext_resource path="res://addons/text_editor/fonts/unifont_upper-13.0.01.ttf" type="DynamicFontData" id=2] 5 | [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] 6 | 7 | [resource] 8 | size = 12 9 | use_filter = true 10 | font_data = ExtResource( 1 ) 11 | fallback/0 = ExtResource( 3 ) 12 | fallback/1 = ExtResource( 2 ) 13 | -------------------------------------------------------------------------------- /addons/text_editor/fonts/font_r.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=4 format=2] 2 | 3 | [ext_resource path="res://addons/text_editor/fonts/RobotoMono-Regular.ttf" type="DynamicFontData" id=1] 4 | [ext_resource path="res://addons/text_editor/fonts/unifont_upper-13.0.01.ttf" type="DynamicFontData" id=2] 5 | [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] 6 | 7 | [resource] 8 | size = 12 9 | use_filter = true 10 | font_data = ExtResource( 1 ) 11 | fallback/0 = ExtResource( 3 ) 12 | fallback/1 = ExtResource( 2 ) 13 | -------------------------------------------------------------------------------- /addons/text_editor/TE_MetaInfo.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends "res://addons/text_editor/TE_RichTextLabel.gd" 3 | 4 | func _ready(): 5 | var _e 6 | _e = editor.connect("file_selected", self, "_file_selected") 7 | _e = editor.connect("file_saved", self, "_file_saved") 8 | 9 | #func _resized(): 10 | # add_constant_override("table_hseparation", int(rect_size.x / 6.0)) 11 | 12 | func _file_selected(_file_path:String): 13 | yield(get_tree(), "idle_frame") 14 | _redraw() 15 | 16 | func _file_saved(_file_path:String): 17 | _redraw() 18 | 19 | func _redraw(): 20 | if not visible: 21 | return 22 | 23 | var tab = editor.get_selected_tab() 24 | if tab: 25 | tab.helper.generate_meta(tab, self) 26 | -------------------------------------------------------------------------------- /addons/text_editor/plugin.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends EditorPlugin 3 | 4 | const TEPanel:PackedScene = preload("res://addons/text_editor/TextEditor.tscn") 5 | var panel:Node 6 | 7 | func get_plugin_name(): return "Text" 8 | func get_plugin_icon(): return get_editor_interface().get_base_control().get_icon("Font", "EditorIcons") 9 | func has_main_screen(): return true 10 | 11 | func _enter_tree(): 12 | panel = TEPanel.instance() 13 | panel.plugin = self 14 | panel.plugin_hint = true 15 | get_editor_interface().get_editor_viewport().add_child(panel) 16 | make_visible(false) 17 | 18 | func _exit_tree(): 19 | if panel: 20 | panel.queue_free() 21 | 22 | func make_visible(visible): 23 | if panel: 24 | panel.visible = visible 25 | 26 | 27 | -------------------------------------------------------------------------------- /addons/text_editor/TE_LineEdit.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends LineEdit 3 | 4 | onready var editor:TE_Editor = owner 5 | var fr:FuncRef 6 | 7 | func _ready(): 8 | var _e 9 | _e = connect("text_entered", self, "_enter") 10 | _e = connect("focus_exited", self, "_lost_focus") 11 | 12 | add_font_override("font", editor.FONT_R) 13 | 14 | func _unhandled_key_input(e): 15 | if not editor.is_plugin_active(): 16 | return 17 | 18 | if visible and e.scancode == KEY_ESCAPE and e.pressed: 19 | fr = null 20 | hide() 21 | get_tree().set_input_as_handled() 22 | 23 | func display(t:String, obj:Object, fname:String): 24 | text = t 25 | select_all() 26 | fr = funcref(obj, fname) 27 | show() 28 | call_deferred("grab_focus") 29 | 30 | func _lost_focus(): 31 | fr = null 32 | hide() 33 | 34 | func _enter(t:String): 35 | if fr: 36 | fr.call_func(t) 37 | hide() 38 | -------------------------------------------------------------------------------- /addons/text_editor/TE_TabScroller.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TabContainer 3 | 4 | onready var editor:TE_Editor = owner 5 | 6 | var mouse_over:bool = false 7 | 8 | func _ready(): 9 | var _e 10 | _e = connect("mouse_entered", self, "set", ["mouse_over", true]) 11 | _e = connect("mouse_exited", self, "set", ["mouse_over", false]) 12 | 13 | func _input(e): 14 | if mouse_over and e is InputEventMouseButton and e.pressed: 15 | if e.button_index == BUTTON_WHEEL_DOWN: 16 | prev() 17 | get_tree().set_input_as_handled() 18 | 19 | elif e.button_index == BUTTON_WHEEL_UP: 20 | next() 21 | get_tree().set_input_as_handled() 22 | 23 | # if not editor.is_plugin_active(): 24 | # return 25 | 26 | func prev(): 27 | set_current_tab(wrapi(current_tab - 1, 0, get_child_count())) 28 | 29 | func next(): 30 | set_current_tab(wrapi(current_tab + 1, 0, get_child_count())) 31 | 32 | -------------------------------------------------------------------------------- /addons/text_editor/icons/icon_red.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_red.png-68ce187cb535e9b040383f41e156a86c.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/text_editor/icons/icon_red.png" 13 | dest_files=[ "res://.import/icon_red.png-68ce187cb535e9b040383f41e156a86c.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/text_editor/icons/icon_blue.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_blue.png-4b6f1e67dbe59cc990b970ce70a743ca.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/text_editor/icons/icon_blue.png" 13 | dest_files=[ "res://.import/icon_blue.png-4b6f1e67dbe59cc990b970ce70a743ca.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/text_editor/icons/icon_green.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_green.png-01008ffea524815bdbefdafa2b021148.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/text_editor/icons/icon_green.png" 13 | dest_files=[ "res://.import/icon_green.png-01008ffea524815bdbefdafa2b021148.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/text_editor/icons/icon_yellow.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_yellow.png-4fd0044497c78a10c7a9305f6c12f846.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/text_editor/icons/icon_yellow.png" 13 | dest_files=[ "res://.import/icon_yellow.png-4fd0044497c78a10c7a9305f6c12f846.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/text_editor/TE_DragLabel.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends RichTextLabel 3 | 4 | var editor:TE_Editor 5 | var click_pos:Vector2 6 | 7 | func _init(text): 8 | set_bbcode(text) 9 | visible = false 10 | 11 | func _ready(): 12 | add_font_override("normal_font", editor.FONT_R) 13 | click_pos = get_global_mouse_position() 14 | # add_font_override("bold_font", editor.FONT_B) 15 | # add_font_override("italics_font", editor.FONT_I) 16 | # add_font_override("bold_italics_font", editor.FONT_BI) 17 | 18 | rect_size = editor.FONT_R.get_string_size(text) 19 | rect_size += Vector2(16, 16) 20 | 21 | func _process(_delta): 22 | var mp = get_global_mouse_position() 23 | set_visible(mp.distance_to(click_pos) > 16.0) 24 | set_global_position(mp + Vector2(16, 8)) 25 | 26 | func _input(e): 27 | if e is InputEventMouseButton: 28 | if (e.button_index == BUTTON_LEFT and not e.pressed) or (e.button_index == BUTTON_RIGHT and e.pressed): 29 | queue_free() 30 | -------------------------------------------------------------------------------- /addons/text_editor/ext/ext_ini.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TE_ExtensionHelper 3 | 4 | func apply_colors(e:TE_Editor, t:TextEdit): 5 | .apply_colors(e, t) 6 | # symbols 7 | t.add_color_region("[", "]", e.color_symbol, false) 8 | 9 | # string 10 | t.add_color_region('"', '"', e.color_var, false) 11 | 12 | # comment 13 | t.add_color_region(';', '', e.color_comment, true) 14 | 15 | func get_symbols(t:String) -> Dictionary: 16 | var out = .get_symbols(t) 17 | var last = add_symbol() 18 | var lines = t.split("\n") 19 | var i = 0 20 | 21 | while i < len(lines): 22 | # symbols 23 | if lines[i].begins_with("["): 24 | var name = lines[i].split("[", true, 1)[1].split("]", true, 1)[0] 25 | last = add_symbol(i, 0, name) 26 | 27 | # tags 28 | elif lines[i].begins_with(";") and "#" in lines[i]: 29 | for t in lines[i].substr(1).split("#"): 30 | t = t.strip_edges() 31 | if t: 32 | last.tags.append(t) 33 | 34 | i += 1 35 | 36 | return out 37 | -------------------------------------------------------------------------------- /addons/text_editor/TE_MetaTabs.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TabContainer 3 | 4 | onready var editor:TE_Editor = owner 5 | 6 | func _ready(): 7 | if not editor.is_plugin_active(): 8 | return 9 | 10 | set_visible(false) 11 | add_font_override("font", editor.FONT_R) 12 | 13 | func _unhandled_key_input(e): 14 | if not editor.is_plugin_active(): 15 | return 16 | 17 | if e.control and e.pressed: 18 | match e.scancode: 19 | # show this menu 20 | KEY_M: 21 | set_visible(not get_parent().visible) 22 | get_tree().set_input_as_handled() 23 | 24 | # find menu 25 | KEY_F: 26 | set_visible(true) 27 | select_tab($search) 28 | $search/rte.select() 29 | 30 | func set_visible(v:bool): 31 | get_parent().visible = v 32 | 33 | func select_tab(tab:Node): 34 | current_tab = tab.get_index() 35 | 36 | func show_image(file_path:String): 37 | get_parent().visible = true 38 | select_tab($image) 39 | $image/image.texture = TE_Util.load_image(file_path) 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 teebarjunk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /addons/text_editor/ext/ext_rpy.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TE_ExtensionHelper 3 | 4 | func get_tab() -> String: 5 | return " " 6 | 7 | func apply_colors(e:TE_Editor, t:TextEdit): 8 | .apply_colors(e, t) 9 | 10 | for k in "label menu define default scene show with play return jump call".split(" "): 11 | t.add_keyword_color(k, e.color_symbol) 12 | 13 | # strings 14 | t.add_color_region('"', '"', e.color_var) 15 | # bools 16 | t.add_keyword_color("True", e.color_var) 17 | t.add_keyword_color("False", e.color_var) 18 | 19 | # comments 20 | t.add_color_region("#", "", e.color_comment, true) 21 | t.add_color_region("$ ", "", e.color_comment, true) 22 | 23 | func get_symbols(t:String): 24 | var out = .get_symbols(t) 25 | var last = add_symbol() 26 | var lines = t.split("\n") 27 | var i = 0 28 | 29 | while i < len(lines): 30 | # symbols 31 | if lines[i].begins_with("label "): 32 | var key = lines[i].substr(len("label ")).strip_edges() 33 | key = key.substr(0, len(key)-1) 34 | last = add_symbol(i, 0, key) 35 | 36 | elif lines[i].begins_with("menu "): 37 | var key = lines[i].substr(len("menu ")).strip_edges() 38 | key = key.substr(0, len(key)-1) 39 | last = add_symbol(i, 0, key) 40 | 41 | # tags 42 | elif "#" in lines[i]: 43 | var p = lines[i].rsplit("#", true, 1)[1] 44 | if "#" in p: 45 | for tag in p.split("#"): 46 | last.tags.append(tag) 47 | 48 | i += 1 49 | 50 | return out 51 | -------------------------------------------------------------------------------- /addons/text_editor/ext/ext_json.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TE_ExtensionHelper 3 | 4 | func toggle_comment(t:TextEdit, head:String="/*", tail:String="*/"): 5 | return .toggle_comment(t, head, tail) 6 | 7 | func get_symbols(t:String): 8 | var out = .get_symbols(t) 9 | var last = add_symbol() 10 | var lines = t.split("\n") 11 | var i = 0 12 | 13 | while i < len(lines): 14 | # symbols 15 | if "\": {" in lines[i]: 16 | var key = lines[i].split("\": {", true, 1)[0].rsplit("\"", true, 0)[1] 17 | var deep = max(0, len(lines[i]) - len(lines[i].strip_edges(true, false)) - 1) 18 | last = add_symbol(i, deep, key) 19 | 20 | elif '"#": "' in lines[i]: 21 | for tag in lines[i].split('"#": "', true, 1)[1].split('"', true, 1)[0].split("#"): 22 | tag = tag.strip_edges() 23 | if tag: 24 | last.tags.append(tag) 25 | 26 | elif '"tags": "' in lines[i]: 27 | for tag in lines[i].split('"tags": "', true, 1)[1].split('"', true, 1)[0].split("#"): 28 | tag = tag.strip_edges() 29 | if tag: 30 | last.tags.append(tag) 31 | 32 | i += 1 33 | 34 | return out 35 | 36 | func apply_colors(e:TE_Editor, t:TextEdit): 37 | .apply_colors(e, t) 38 | 39 | # vars 40 | t.add_color_region(' "', '"', e.color_varname) 41 | t.add_color_region('"', '"', e.color_varname) 42 | t.add_keyword_color("true", e.color_var) 43 | t.add_keyword_color("false", e.color_var) 44 | t.add_keyword_color("null", e.color_var) 45 | 46 | # comments 47 | # t.add_color_region("/*", "*/", e.color_comment) 48 | t.add_color_region('\t"#"', ",", e.color_comment, false) 49 | -------------------------------------------------------------------------------- /addons/text_editor/TE_TagsPanel.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends "res://addons/text_editor/TE_RichTextLabel.gd" 3 | 4 | func _ready(): 5 | var _e 6 | _e = editor.connect("symbols_updated", self, "_redraw") 7 | _e = editor.connect("tags_updated", self, "_redraw") 8 | 9 | add_font_override("normal_font", editor.FONT_R) 10 | add_font_override("bold_font", editor.FONT_B) 11 | add_font_override("italics_font", editor.FONT_I) 12 | add_font_override("bold_italics_font", editor.FONT_BI) 13 | 14 | # hint 15 | theme = Theme.new() 16 | theme.set_font("font", "TooltipLabel", editor.FONT_R) 17 | 18 | call_deferred("_redraw") 19 | 20 | func _clicked(args:Array): 21 | var tag = args[0] 22 | var was_enabled = editor.is_tag_enabled(tag) 23 | 24 | if not Input.is_key_pressed(KEY_CONTROL): 25 | editor.tags_enabled.clear() 26 | 27 | editor.enable_tag(tag, not was_enabled) 28 | 29 | #func sort_tags(tags:Dictionary): 30 | # var sorter:Array = [] 31 | # for tag in tags: 32 | # sorter.append([tag, tags[tag]]) 33 | # 34 | # sorter.sort_custom(self, "_sort_tags") 35 | # 36 | # tags.clear() 37 | # for item in sorter: 38 | # tags[item[0]] = item[1] 39 | # return tags 40 | # 41 | #func _sort_tags(a, b): 42 | # return a[0] < b[0] 43 | 44 | func _redraw(): 45 | var tab = editor.get_selected_tab() 46 | var tags = editor.tags 47 | var tab_tags = {} if not tab else tab.tags 48 | 49 | # sort_tags(tags) 50 | 51 | if not tags: 52 | set_bbcode("[color=#%s][i][center]*No tags*" % [Color.webgray.to_html()]) 53 | 54 | else: 55 | var t:PoolStringArray = PoolStringArray() 56 | var count_color1 = Color.tomato.to_html() 57 | var count_color2 = Color.tomato.darkened(.75).to_html() 58 | for tag in tags: 59 | var count = editor.tags[tag] 60 | var enabled = editor.is_tag_enabled(tag) 61 | 62 | var x = tag 63 | var color = editor.color_text 64 | var dim = 0.75 65 | 66 | if tag in tab_tags: 67 | color = editor.color_tag 68 | x = b(x) 69 | dim = 0.6 70 | 71 | if enabled: 72 | x = x 73 | else: 74 | x = clr(x, color.darkened(dim)) 75 | 76 | t.append(meta(x, [tag], "%s x%s" % [tag, count] )) 77 | 78 | set_bbcode("[center]" + t.join(" ")) 79 | -------------------------------------------------------------------------------- /addons/text_editor/ext/TE_ExtensionHelper.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Resource 3 | class_name TE_ExtensionHelper 4 | 5 | var symbols:Dictionary = {} 6 | 7 | func get_tab() -> String: 8 | return " " 9 | 10 | func generate_meta(t:TextEdit, r:RichTextLabel): 11 | var chars = TE_Util.commas(len(t.text)) 12 | var words = TE_Util.commas(len(t.text.split(" ", false))) 13 | var lines = TE_Util.commas(len(TE_Util.split_many(t.text, ".?!\n", false))) 14 | var bytes = TE_Util.file_size(t.file_path) 15 | 16 | r.set_bbcode(r.table([ 17 | ["chars", "words", "lines", "bytes"], 18 | [chars, words, lines, bytes] 19 | ])) 20 | 21 | func toggle_comment(t:TextEdit, head:String="", tail:String=""): 22 | var wasnt_selected:bool = false 23 | var cursor_l 24 | var cursor_c 25 | 26 | if not t.is_selection_active(): 27 | var c = t.cursor_get_column() 28 | t.insert_text_at_cursor(head + tail) 29 | t.cursor_set_column(c + len(head)) 30 | return 31 | # var l = t.cursor_get_line() 32 | # var lt = t.get_line(l) 33 | # wasnt_selected = lt.strip_edges() == "" 34 | # cursor_l = t.cursor_get_line() 35 | # cursor_c = t.cursor_get_column() 36 | # var s = len(lt) - len(lt.strip_edges(true, false)) 37 | # t.select(l, s, l, len(t.get_line(l))) 38 | 39 | # if not t.is_selection_active(): 40 | # return 41 | 42 | var l1 = t.get_selection_from_line() 43 | var c1 = t.get_selection_from_column() 44 | var old = t.get_selection_text() 45 | var new 46 | 47 | if TE_Util.is_wrapped(old, head, tail): 48 | new = TE_Util.unwrap(old, head, tail) 49 | else: 50 | new = TE_Util.wrap(old, head, tail) 51 | 52 | t.insert_text_at_cursor(new) 53 | 54 | if wasnt_selected: 55 | t.deselect() 56 | t.cursor_set_line(cursor_l) 57 | t.cursor_set_column(cursor_c+len(head)) 58 | 59 | else: 60 | var l = new.split("\n") 61 | var l2 = l1 + len(l)-1 62 | var c2 = c1 + len(l[-1]) 63 | t.select(l1, c1, l2, c2) 64 | 65 | func add_symbol(line:int=-1, deep:int=0, name:String="") -> Dictionary: 66 | var symbol = { deep=deep, name=name, tags=[] } 67 | symbols[line] = symbol 68 | return symbol 69 | 70 | func get_symbols(t:String) -> Dictionary: 71 | symbols = {} 72 | return symbols 73 | 74 | #func get_symbol_names(s:Dictionary): 75 | # var out = [] 76 | # for k in s: 77 | # if k != -1: 78 | # out.append(s[k].name) 79 | # return out 80 | 81 | func get_tag_counts(s:Dictionary) -> Dictionary: 82 | var out = {} 83 | for k in s: 84 | for tag in s[k].tags: 85 | if not tag in out: 86 | out[tag] = 1 87 | else: 88 | out[tag] += 1 89 | return out 90 | 91 | func apply_colors(e, t:TextEdit): 92 | t.add_color_override("font_color", e.color_text) 93 | t.add_color_override("number_color", e.color_var) 94 | t.add_color_override("member_variable_color", e.color_var) 95 | -------------------------------------------------------------------------------- /addons/text_editor/ext/ext_yaml.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TE_ExtensionHelper 3 | 4 | func get_tab() -> String: 5 | return " " 6 | 7 | func _is_commented(lines) -> bool: 8 | for i in len(lines): 9 | if not lines[i].strip_edges(): 10 | continue 11 | if not lines[i].strip_edges(true, false).begins_with("# "): 12 | return false 13 | return true 14 | 15 | func toggle_comment(t:TextEdit, head:String="", tail:String=""): 16 | if not t.is_selection_active(): 17 | var l = t.cursor_get_line() 18 | var lt = t.get_line(l) 19 | var s = len(lt) - len(lt.strip_edges(true, false)) 20 | t.select(l, s, l, len(t.get_line(l))) 21 | 22 | var l1 = t.get_selection_from_line() 23 | var c1 = t.get_selection_from_column() 24 | var old = t.get_selection_text() 25 | var new = old.split("\n") 26 | 27 | if _is_commented(new): 28 | for i in len(new): 29 | if "# " in new[i]: 30 | var p = new[i].split("# ", true, 1) 31 | new[i] = p[0] + p[1] 32 | else: 33 | for i in len(new): 34 | if not new[i].strip_edges(): 35 | continue 36 | var space = TE_Util.get_whitespace_head(new[i]) 37 | new[i] = space + "# " + new[i].strip_edges(true, false) 38 | 39 | new = new.join("\n") 40 | 41 | t.insert_text_at_cursor(new) 42 | var l = new.split("\n") 43 | var l2 = l1 + len(l)-1 44 | var c2 = c1 + len(l[-1]) 45 | t.select(l1, c1, l2, c2) 46 | 47 | return [old, new] 48 | 49 | func apply_colors(e:TE_Editor, t:TextEdit): 50 | .apply_colors(e, t) 51 | 52 | # strings 53 | t.add_color_region('"', '"', e.color_var) 54 | # bools 55 | t.add_keyword_color("true", e.color_var) 56 | t.add_keyword_color("false", e.color_var) 57 | 58 | # null 59 | t.add_keyword_color("~", e.color_var) 60 | 61 | # array element 62 | t.add_color_region("- ", "", e.color_text.darkened(.25), true) 63 | 64 | # comments 65 | t.add_color_region("#", "", e.color_comment, true) 66 | 67 | 68 | func get_symbols(t:String) -> Dictionary: 69 | var out = .get_symbols(t) 70 | var last = add_symbol() 71 | var lines = t.split("\n") 72 | var i = 0 73 | 74 | while i < len(lines): 75 | # find objects to use as symbols 76 | if ":" in lines[i]: 77 | var p = lines[i].split(":", true, 1) 78 | var r = p[1].strip_edges() 79 | if not r or r.begins_with("{") or r.begins_with("#"): 80 | var name = p[0].strip_edges() 81 | var deep = max(0, len(lines[i]) - len(lines[i].strip_edges(true, false))) 82 | last = add_symbol(i, deep, name) 83 | 84 | # find tags inside comments 85 | if "# " in lines[i]: 86 | var p = lines[i].split("# ", true, 1) 87 | if p[0].count("\"") % 2 != 0: 88 | pass 89 | 90 | elif "#" in p[1]: 91 | for tag in p[1].split("#", true, 1)[1].split("#"): 92 | tag = tag.strip_edges() 93 | if tag: 94 | last.tags.append(tag) 95 | 96 | elif '"#": "' in lines[i]: 97 | for tag in lines[i].splti('"#": "', true, 1)[1].split('"', true, 1)[0].split("#"): 98 | tag = tag.strip_edges() 99 | if tag: 100 | last.tags.append(tag) 101 | 102 | i += 1 103 | 104 | return out 105 | -------------------------------------------------------------------------------- /addons/text_editor/TE_FileTabs.gd: -------------------------------------------------------------------------------- 1 | extends TabContainer 2 | 3 | onready var editor:TE_Editor = owner 4 | var mouse:bool = false 5 | var last_tab_index:int = -1 6 | var tab_menu:PopupMenu 7 | 8 | func _ready(): 9 | if not editor.is_plugin_active(): 10 | return 11 | 12 | var _e 13 | _e = connect("mouse_entered", self, "set", ["mouse", true]) 14 | _e = connect("mouse_exited", self, "set", ["mouse", false]) 15 | _e = connect("tab_changed", self, "_tab_changed") 16 | _e = connect("pre_popup_pressed", self, "update_popup") 17 | 18 | add_font_override("font", editor.FONT_R) 19 | 20 | tab_menu = owner.get_node("popup_tab_menu") 21 | tab_menu.connect("index_pressed", self, "_popup_selected") 22 | 23 | func _tab_changed(index): 24 | var tab = get_child(index) 25 | tab.grab_focus() 26 | 27 | last_tab_index = index 28 | 29 | func _popup_selected(index:int): 30 | var tindex := tab_menu.get_item_id(index) 31 | if tindex >= 100: 32 | current_tab = tindex - 100 33 | return 34 | 35 | match tindex: 36 | 0: # close 37 | get_child(hovered_tab_index).close() 38 | 39 | 1: # close others 40 | var all_tabs = owner.get_tabs() 41 | var hovered = get_child(hovered_tab_index) 42 | for tab in all_tabs: 43 | if tab != hovered: 44 | tab.close() 45 | 46 | 2: # close left 47 | var all_tabs = owner.get_tabs() 48 | for i in range(0, hovered_tab_index): 49 | all_tabs[i].close() 50 | current_tab = 0 51 | 52 | 3: # close right 53 | var all_tabs = owner.get_tabs() 54 | for i in range(hovered_tab_index+1, len(all_tabs)): 55 | all_tabs[i].close() 56 | 57 | var hovered_tab_index:int 58 | func update_popup(index:int=current_tab): 59 | var all_tabs = owner.get_tabs() 60 | 61 | hovered_tab_index = index 62 | 63 | tab_menu.clear() 64 | tab_menu.rect_size = Vector2.ZERO 65 | tab_menu.add_item("Close", 0) 66 | tab_menu.add_item("Close others", 1) 67 | 68 | if index > 0: 69 | tab_menu.add_item("Close all to left", 2) 70 | 71 | if index < len(all_tabs)-1: 72 | tab_menu.add_item("Close all to right", 3) 73 | 74 | tab_menu.add_separator() 75 | 76 | var i = 0 77 | for tab in owner.get_tabs(): 78 | tab_menu.add_item(tab.name, 100+i) 79 | i += 1 80 | 81 | func _input(e): 82 | if not editor.is_plugin_active(): 83 | return 84 | 85 | if mouse and e is InputEventMouseButton and e.pressed: 86 | if e.button_index == BUTTON_WHEEL_DOWN: 87 | prev() 88 | get_tree().set_input_as_handled() 89 | 90 | elif e.button_index == BUTTON_WHEEL_UP: 91 | next() 92 | get_tree().set_input_as_handled() 93 | 94 | elif e.button_index == BUTTON_RIGHT: 95 | var index := get_tab_idx_at_point(get_local_mouse_position()) 96 | if index != -1: 97 | update_popup(index) 98 | tab_menu.rect_global_position = get_global_mouse_position() 99 | tab_menu.popup() 100 | get_tree().set_input_as_handled() 101 | 102 | if e is InputEventKey and e.pressed and e.control and e.scancode == KEY_TAB: 103 | if e.shift: 104 | prev() 105 | get_tree().set_input_as_handled() 106 | else: 107 | next() 108 | get_tree().set_input_as_handled() 109 | 110 | func prev(): current_tab = wrapi(current_tab - 1, 0, get_child_count()) 111 | func next(): current_tab = wrapi(current_tab + 1, 0, get_child_count()) 112 | 113 | -------------------------------------------------------------------------------- /addons/text_editor/TE_FileInfoLabel.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Node 3 | 4 | const DIM:Color = Color.webgray 5 | const CLR:Color = Color.white 6 | 7 | onready var editor:TE_Editor = owner 8 | var tab:TextEdit = null 9 | var typed:String = "" 10 | var word_count:int = 0 11 | 12 | func _ready(): 13 | var _e 14 | yield(get_tree(), "idle_frame") 15 | _e = editor.tab_parent.connect("tab_changed", self, "_tab_changed") 16 | 17 | editor.override_fonts($l) 18 | editor.override_fonts($m) 19 | editor.override_fonts($r) 20 | 21 | func _tab_changed(index:int): 22 | if tab and is_instance_valid(tab) and not tab.is_queued_for_deletion(): 23 | tab.disconnect("cursor_changed", self, "_cursor_changed") 24 | tab = null 25 | 26 | var new_tab = editor.tab_parent.get_child(index) 27 | if tab == new_tab: 28 | return 29 | tab = new_tab 30 | var _e 31 | _e = tab.connect("cursor_changed", self, "_cursor_changed") 32 | # _e = tab.connect("text_changed", self, "_text_changed") 33 | # 34 | #func _text_changed(): 35 | ## print("text changed") 36 | # pass 37 | 38 | func _input(event): 39 | if event is InputEventKey and event.pressed and tab and is_instance_valid(tab) and tab.has_focus(): 40 | if not event.scancode == KEY_BACKSPACE: 41 | if char(event.scancode) in " .?!-": 42 | word_count += 1 43 | typed = "" 44 | else: 45 | typed += char(event.scancode) 46 | _cursor_changed() 47 | 48 | func _cursor_changed(): 49 | var l_lines:PoolStringArray = PoolStringArray() 50 | var m_lines:PoolStringArray = PoolStringArray() 51 | var r_lines:PoolStringArray = PoolStringArray() 52 | var l:RichTextLabel 53 | 54 | if tab.is_selection_active(): 55 | var seltext:String = tab.get_selection_text() 56 | var words = {} 57 | var word_count:int = TE_Util.count_words(seltext, words, null, false) 58 | m_lines.append(kv("chars", len(seltext))) 59 | m_lines.append(kv("words", word_count)) 60 | 61 | var l1 = tab.get_selection_from_line() + 1 62 | var l2 = tab.get_selection_to_line() + 1 63 | var c1 = tab.get_selection_from_column() 64 | var c2 = tab.get_selection_to_column() 65 | 66 | if l1 == l2: 67 | l_lines.append(kv("line", l1)) 68 | l_lines.append(kv("char", "%s - %s" % [c1, c2])) 69 | 70 | else: 71 | l_lines.append(clr("line: ", DIM) + clr(str(l1), CLR) + clr(":", DIM) + clr(str(c1), CLR)) 72 | l_lines.append(clr("->", Color.webgray)) 73 | l_lines.append(clr("line: ", DIM) + clr(str(l2), CLR) + clr(":", DIM) + clr(str(c2), CLR)) 74 | 75 | m_lines.append(kv("lines", abs(l2 - l1) + 1)) 76 | 77 | else: 78 | l_lines.append(kv("line", tab.cursor_get_line() + 1)) 79 | l_lines.append(kv("char", tab.cursor_get_column())) 80 | 81 | var depth = tab.get_line_symbols(tab.cursor_get_line()) 82 | for i in len(depth): 83 | depth[i] = b(depth[i]) 84 | r_lines.append(depth.join(clr("/", DIM))) 85 | 86 | m_lines.append(kv("typed", word_count)) 87 | 88 | $l.set_bbcode(l_lines.join(" ")) 89 | $m.set_bbcode("[center]" + m_lines.join(" ")) 90 | $r.set_bbcode("[right]" +r_lines.join(" ")) 91 | 92 | func kv(k:String, v) -> String: 93 | var clr2 = Color.white 94 | if v is int: 95 | v = TE_Util.commas(v) 96 | return clr(k + ": ", DIM) + clr(str(v), clr2) 97 | 98 | func b(t:String) -> String: return "[b]%s[/b]" % t 99 | func i(t:String) -> String: return "[i]%s[/i]" % t 100 | func u(t:String) -> String: return "[u]%s[/u]" % t 101 | func clr(t:String, c:Color) -> String: return "[color=#%s]%s[/color]" % [c.to_html(), t] 102 | -------------------------------------------------------------------------------- /addons/text_editor/TE_RichTextLabel.gd: -------------------------------------------------------------------------------- 1 | extends RichTextLabel 2 | 3 | onready var editor:TE_Editor = owner 4 | 5 | var meta_items:Array = [] 6 | var meta_hovered:Array = [] 7 | 8 | class Table: 9 | var table_id:String 10 | var heading:Array = [] 11 | var columns:Array = [] 12 | var _sort_index:int 13 | var _sort_reverse:bool 14 | 15 | func _init(id:String): 16 | table_id = id 17 | 18 | func sort(index:int, reverse:bool): 19 | _sort_index = index 20 | _sort_reverse = reverse 21 | columns.sort_custom(self, "_sort") 22 | 23 | func output(rte:RichTextLabel): 24 | rte.push_table(len(heading)) 25 | for i in len(heading): 26 | rte.push_cell() 27 | rte.push_bold() 28 | rte.push_meta("table|%s|%s" % [table_id, i]) 29 | rte.add_text(heading[i]) 30 | rte.pop() 31 | rte.pop() 32 | rte.pop() 33 | for i in len(columns): 34 | rte.push_cell() 35 | rte.add_text(str(columns[i])) 36 | rte.pop() 37 | rte.pop() 38 | 39 | class RTE: 40 | var rte 41 | var s:String 42 | 43 | func start(st:String): 44 | s = st 45 | return self 46 | 47 | func clr(c:Color): 48 | s = "[color=#%s]%s[/color]" % [c.to_html(), s] 49 | return self 50 | 51 | func meta(type:String, meta, args=null): 52 | var index:int = len(rte.meta_items) 53 | rte.meta_items.append(meta) 54 | s = "[url=%s|%s]%s[/url]" % [type, index, s] 55 | return self 56 | 57 | func out(): 58 | rte.append_bbcode(s) 59 | 60 | func _ready(): 61 | # hint 62 | theme = Theme.new() 63 | theme.set_font("font", "TooltipLabel", editor.FONT_R) 64 | 65 | add_font_override("normal_font", owner.FONT_R) 66 | add_font_override("bold_font", owner.FONT_B) 67 | add_font_override("italics_font", owner.FONT_I) 68 | add_font_override("bold_italics_font", owner.FONT_BI) 69 | 70 | var _e 71 | _e = connect("resized", self, "_resized") 72 | _e = connect("meta_clicked", self, "_meta_clicked") 73 | _e = connect("meta_hover_started", self, "_meta_hover_started") 74 | _e = connect("meta_hover_ended", self, "_meta_hover_ended") 75 | 76 | func _resized(): 77 | pass 78 | 79 | func _clicked(_data): 80 | pass 81 | 82 | func clear(): 83 | .clear() 84 | meta_items.clear() 85 | 86 | func table(rows) -> String: 87 | var cells = "" 88 | var clr = Color.white.darkened(.5).to_html() 89 | for i in len(rows): 90 | if i == 0: 91 | for item in rows[i]: 92 | cells += "[cell][b]%s[/b][/cell][/color]" % item 93 | else: 94 | for item in rows[i]: 95 | cells += "[cell][color=#%s]%s[/color][/cell]" % [clr, item] 96 | return "[center][table=%s]%s[/table][/center]" % [len(rows[0]), cells] 97 | 98 | func b(t:String) -> String: return "[b]%s[/b]" % t 99 | func i(t:String) -> String: return "[i]%s[/i]" % t 100 | func u(t:String) -> String: return "[u]%s[/u]" % t 101 | func clr(t:String, c:Color) -> String: return "[color=#%s]%s[/color]" % [c.to_html(), t] 102 | func center(t:String): return "[center]%s[/center]" % t 103 | 104 | func _meta_hover_started(meta): 105 | var info = meta_items[int(meta)] 106 | var hint = info[1] 107 | meta_hovered = info[0] 108 | if hint: 109 | hint_tooltip = hint 110 | 111 | func _meta_hover_ended(_meta): 112 | meta_hovered = [] 113 | hint_tooltip = "" 114 | 115 | func _meta_clicked(meta): 116 | var info = meta_items[int(meta)] 117 | if info[0]: 118 | _clicked(info[0]) 119 | 120 | func add_meta(args:Array, hint:String) -> int: 121 | var index:int = len(meta_items) 122 | meta_items.append([args, hint]) 123 | return index 124 | 125 | func meta(t:String, args:Array=[], hint:String="") -> String: 126 | var index:int = add_meta(args, hint) 127 | return "[url=%s]%s[/url]" % [index, t] 128 | -------------------------------------------------------------------------------- /addons/text_editor/TE_SymbolsList.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends "res://addons/text_editor/TE_RichTextLabel.gd" 3 | 4 | var hscrolls:Dictionary = {} 5 | var selected_line:int = 0 6 | var current_file:String = "" 7 | 8 | var filter:String = "" 9 | export var p_filter:NodePath 10 | 11 | func _ready(): 12 | var _e 13 | _e = editor.connect("symbols_updated", self, "_redraw") 14 | _e = editor.connect("tags_updated", self, "_redraw") 15 | _e = editor.connect("file_selected", self, "_file_selected") 16 | _e = editor.connect("file_closed", self, "_file_closed") 17 | _e = editor.connect("file_renamed", self, "_file_renamed") 18 | _e = editor.connect("selected_symbol_line", self, "_selected_symbol_line") 19 | _e = get_v_scroll().connect("value_changed", self, "_scrolling") 20 | 21 | var le:LineEdit = get_node(p_filter) 22 | _e = le.connect("text_changed", self, "_filter_changed") 23 | le.add_font_override("font", editor.FONT_R) 24 | 25 | add_font_override("normal_font", editor.FONT_R) 26 | add_font_override("bold_font", editor.FONT_B) 27 | add_font_override("italics_font", editor.FONT_I) 28 | add_font_override("bold_italics_font", editor.FONT_BI) 29 | 30 | call_deferred("_redraw") 31 | 32 | func _filter_changed(t:String): 33 | filter = t.to_lower() 34 | _redraw() 35 | 36 | func _selected_symbol_line(line:int): 37 | selected_line = clamp(line, 0, get_line_count()) 38 | scroll_to_line(clamp(line-1, 0, get_line_count()-1)) 39 | _redraw() 40 | 41 | func _file_selected(file_path:String): 42 | current_file = file_path 43 | yield(get_tree(), "idle_frame") 44 | get_v_scroll().value = hscrolls.get(file_path, 0) 45 | 46 | func _file_renamed(old:String, new:String): 47 | current_file = new 48 | yield(get_tree(), "idle_frame") 49 | _redraw() 50 | 51 | func _file_closed(file_path:String): 52 | if file_path == current_file: 53 | current_file = "" 54 | _redraw() 55 | 56 | func _scrolling(v): 57 | hscrolls[editor.get_selected_file()] = get_v_scroll().value 58 | 59 | func _clicked(args:Array): 60 | var te:TextEdit = editor.get_selected_tab() 61 | 62 | # select entire symbol block? 63 | if Input.is_key_pressed(KEY_CONTROL): 64 | var tab = editor.get_selected_tab() 65 | var symbols = {} if not tab else tab.symbols 66 | var line_index:int = args[1] 67 | var symbol_index:int = symbols.keys().find(line_index) 68 | var next_line:int 69 | 70 | # select sub symbol blocks? 71 | if not Input.is_key_pressed(KEY_SHIFT): 72 | var deep = symbols[line_index].deep 73 | 74 | while symbol_index < len(symbols)-1 and symbols.values()[symbol_index+1].deep > deep: 75 | symbol_index += 1 76 | 77 | if symbol_index == len(symbols)-1: 78 | next_line = tab.get_line_count()-1 79 | 80 | else: 81 | next_line = symbols.keys()[symbol_index+1]-1 82 | 83 | tab.select(line_index, 0, next_line, len(tab.get_line(next_line))) 84 | te.goto_line(line_index) 85 | 86 | else: 87 | te.goto_line(args[1]) 88 | 89 | func _redraw(): 90 | var tab = editor.get_selected_tab() 91 | var symbols = {} if not tab else tab.symbols 92 | var spaces = PoolStringArray([ 93 | "- ", 94 | " - ", 95 | " - " 96 | ]) 97 | var colors = PoolColorArray([ 98 | Color.white, 99 | Color.white.darkened(.25), 100 | Color.white.darkened(.5) 101 | ]) 102 | 103 | # no symbols 104 | if not symbols or len(symbols) == 1: 105 | set_bbcode("[color=#%s][i][center]*No symbols*" % [Color.webgray.to_html()]) 106 | 107 | else: 108 | var t = PoolStringArray() 109 | var i = -1 110 | for line_index in symbols: 111 | i += 1 112 | if line_index == -1: 113 | continue # special file chapter 114 | 115 | var symbol_info = symbols[line_index] 116 | var deep = symbol_info.deep 117 | var space = "" if not deep else clr("-".repeat(deep), Color.white.darkened(.75)) 118 | var cl = Color.white 119 | 120 | if filter and not filter in symbol_info.name.to_lower(): 121 | continue 122 | 123 | if symbol_info.name.begins_with("*") and symbol_info.name.ends_with("*"): 124 | cl = editor.get_symbol_color(deep, -.33) 125 | 126 | elif symbol_info.name.begins_with('"') and symbol_info.name.ends_with('"'): 127 | cl = editor.get_symbol_color(deep, .33) 128 | 129 | else: 130 | cl = editor.get_symbol_color(deep) 131 | 132 | if not editor.is_tagged_or_visible(symbol_info.tags): 133 | cl = cl.darkened(.7) 134 | 135 | var tags = "" if not symbol_info.tags else PoolStringArray(symbol_info.tags).join(", ") 136 | var text = clr(meta(space + symbol_info.name, [symbol_info, line_index], tags), cl) 137 | if i == selected_line: 138 | text = b(u(text)) 139 | 140 | t.append(text) 141 | 142 | set_bbcode(t.join("\n")) 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Text Editor 2 | Version `1.12` 3 | 4 | ![](README/readme_preview.png) 5 | 6 | ***Warning: Use at your own risk. Backup your files before testing.*** 7 | 8 | # Features 9 | - Multi file tab system. 10 | - File browser filtering. 11 | - Highlighting for common formats (`md` `json` `ini`...) 12 | - Tag [System](#mini-features-tags). 13 | - File Management: 14 | - Creation. 15 | - Renaming. 16 | - Recycling. 17 | - Moving. 18 | - Search files. 19 | - Image previews. 20 | - Auto save/load settings. 21 | - Many little *Ease of Life* [features](#mini-features). 22 | 23 | # Controls 24 | - `ctrl + N` New file. 25 | - `ctrl + W` Close file. 26 | - `ctrl + shift + W` Open last closed file . 27 | - `ctrl + tab` Select next open file. 28 | - `ctrl + shift + tab` Select last open file. 29 | - `ctrl + mouse wheel` Adjust font size. 30 | - `ctrl + shift + mouse wheel` Adjust ui font size. 31 | - `ctrl + up` & `ctrl + down` Move selected lines. 32 | - `ctrl + /` Toggle line comments. 33 | - `ctrl + M` Toggle file meta info. 34 | - `ctrl + F` Search for text in all files. 35 | - `ctrl + shift + 0-9` Create hotkey for selected file. 36 | - `ctrl + 0-9` Load hotkeyed file. 37 | 38 | ## Symbol View 39 | - `ctrl + click` Select entire block + children. 40 | - `ctrl + shift + click` Select block without children. 41 | 42 | ## Editor View 43 | - `ctrl + click` anywhere: Scroll to nearest symbol in symbol view. 44 | - `ctrl + click` inside brackets: Goto local file. 45 | - `ctrl + shift +` 46 | - `U` Make selection uppercase. 47 | - `L` Make selection lowercase. 48 | - `O` Make selection capitalized. 49 | - `P` Make selection variable: `My text -> my_text` 50 | 51 | # Symbols and Tags 52 | *Symbols* are like *Table of Contents* for a file. 53 | 54 | - `Markdown` uses headings `# Heading` 55 | - `JSON` uses Dictionaries `"object": {` 56 | - `YAML` uses Dictionaries `object: ` 57 | - `ini` `cfg` use headings `[heading]` 58 | 59 | Symbols can have *Tags*. Tags are added with comments. 60 | 61 | - `Markdown` uses `` 62 | - `JSON` uses `"#": "#tag1 #tag2"` 63 | - `YAML` uses `# #tag1 #tag2` or `"#": "#tag1 #tag2"` 64 | - `ini` `cfg` uses `; #tag1 #tag2` 65 | 66 | Symbols are per file, tags are shared across files. 67 | 68 | When a file is opened with tags, they show up in bottom right *Tag Container*. 69 | 70 | Click them to toggle on and off.\ 71 | This will then highlight *Files* and *Symbols* that have that tag. 72 | 73 | # Todo 74 | - [x] `1.1` Preserve folders open/close state. 75 | - [x] `1.3` Search all files. 76 | - [x] `1.7` Search file. 77 | - [ ] Find and replace. 78 | - [x] `1.7` Improve meta data based on format. 79 | - [x] `1.2` Recycle folders. 80 | - [x] `1.2` Unrecylce. (Toggle `view/directories/.trash` and press green arrow.) 81 | - [ ] JSON formatting. 82 | - [ ] JSON error testing. 83 | - [ ] Color themes. 84 | 85 | # Mini features 86 | 87 | ## File List 88 | ### Colorize Folder 89 | You can colorize files in a folder for easier identification. Right click a folder and select a color. 90 | The `tab colors` toggle at the top will toggle tabs colorized by folder. 91 | 92 | ### Content Preview 93 | You can preview the contents of a file by `ctrl + click`ing it. 94 | 95 | The list is clickable, so you can go straight to a section of the file. 96 | 97 | When using the filter, contents will be scanned. 98 | 99 | ## Content List (Symbols) 100 | ### Selecting Sections 101 | `ctrl + click`ing on a symbol will select all lines contained in it, and it's childrens. 102 | 103 | `ctrl + shift + click` a symbol will only select it's lines, not it's childrens. 104 | 105 | ## File Editor 106 | ### Follow Link 107 | You can follow Markdown links by `ctrl + click`ing on them. 108 | 109 | ## Tags 110 | The tag list displays all tags throughout the files. 111 | 112 | To add a tag to a file, include a comment, with a hashtag: 113 | - `.md`: `` 114 | - `.json`: `{ "#": "#tag1 #tag2 }` 115 | - `.ini` `.cfg`: `; #tag1 #tag2` 116 | - `.yaml`: `# #tag1 #tag2` 117 | 118 | `click` a tag to select it. 119 | 120 | All files in the File List and symbols in the Symbol List containing the tag, will be highlighted. 121 | 122 | `ctrl + click` to select multiple tags at once. 123 | 124 | ## Meta Panel 125 | Toggle the meta panel with `ctrl + M`. 126 | 127 | ### Meta 128 | The meta tab updates whenever you make a save. 129 | 130 | It lists some information on the contents of your file. 131 | 132 | Currently it mostly only works for Markdown. 133 | 134 | ### Search 135 | todo 136 | 137 | ### System 138 | Hitting refrsh will list all files in a table with sortable columns. 139 | 140 | Select a column to sort on: 141 | - Chapter count. 142 | - Word count. 143 | - Unique words. 144 | - Progress. 145 | - Time since modified. 146 | 147 | ### Image 148 | In Markdown files (`.md`) you can `ctrl + click` an image to preview it. 149 | 150 | Images look like: `![](image_url.png)` in Markdown. 151 | 152 | # Icon credit 153 | Files and folders icons created by Uniconlabs - Flaticon -------------------------------------------------------------------------------- /addons/text_editor/TE_Search.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends "res://addons/text_editor/TE_RichTextLabel.gd" 3 | 4 | onready var line_edit:LineEdit = get_parent().get_node("c/le") 5 | onready var all_toggle:CheckBox = get_parent().get_node("c/all") 6 | onready var case_toggle:CheckBox = get_parent().get_node("c/case") 7 | 8 | var console_urls:Array = [] 9 | var last_search:String = "" 10 | 11 | func _ready(): 12 | var _e 13 | _e = line_edit.connect("text_entered", self, "_text_entered") 14 | 15 | # fix fonts 16 | line_edit.add_font_override("font", editor.FONT_R) 17 | all_toggle.add_font_override("font", editor.FONT_R) 18 | case_toggle.add_font_override("font", editor.FONT_R) 19 | 20 | func select(): 21 | line_edit.grab_focus() 22 | line_edit.grab_click_focus() 23 | line_edit.select_all() 24 | 25 | func _clicked(args): 26 | match args[0]: 27 | "goto": 28 | var tab:TextEdit = editor.open_file(args[1]) 29 | editor.select_file(args[1]) 30 | yield(get_tree(), "idle_frame") 31 | # goto line 32 | var hfrom = int(args[2]) 33 | var line = int(args[3]) 34 | tab.goto_line(hfrom) 35 | tab.goto_line(line, false) 36 | # select area 37 | var from = int(args[4]) 38 | var lenn = int(args[5]) 39 | tab.select(line, from, line, from + lenn) 40 | tab.cursor_set_line(line) 41 | tab.cursor_set_column(from) 42 | 43 | func _text_entered(search_for:String): 44 | last_search = search_for 45 | clear() 46 | var found:Dictionary = _search(search_for) 47 | var bbcode:PoolStringArray = PoolStringArray() 48 | var fpaths = found.keys() 49 | 50 | for k in len(fpaths): 51 | if k != 0: 52 | bbcode.append("") 53 | 54 | var file_path:String = fpaths[k] 55 | var l = clr("%s/%s " % [k+1, len(fpaths)], Color.orange) + clr(file_path.get_file(), Color.yellow) 56 | l = meta(l, ["goto", file_path, 0, 0, 0], file_path) 57 | bbcode.append(l) 58 | 59 | var all_found = found[file_path] 60 | for j in len(all_found): 61 | var result = found[file_path][j] 62 | var got_lines = result[0] 63 | var highlight_from = result[1] 64 | var line_index = result[2] 65 | var char_index = result[3] 66 | 67 | bbcode.append(clr(" %s/%s" % [j+1, len(all_found)], Color.orange)) 68 | 69 | for i in len(got_lines): 70 | l = "" 71 | var highlight = got_lines[i][0] 72 | var lindex = got_lines[i][1] 73 | var ltext = got_lines[i][2] 74 | 75 | if highlight: 76 | # var line:String = ltext 77 | # var head:String = line.substr(0, char_index) 78 | # var midd:String = line.substr(char_index, len(search_for)) 79 | # var tail:String = line.substr(char_index+len(search_for)) 80 | # head = clr(head, Color.white.darkened(.25)) 81 | # midd = b(clr(midd, Color.white)) 82 | # tail = clr(tail, Color.white.darkened(.25)) 83 | 84 | var h = TE_Util.highlight(ltext, char_index, len(search_for), Color.white.darkened(.25), Color.white) 85 | 86 | l = "\t" + clr(str(lindex) + ": ", Color.white.darkened(.25)) + h 87 | 88 | else: 89 | l = "\t" + clr(str(lindex) + ": ", Color.white.darkened(.65)) + clr(ltext, Color.white.darkened(.5)) 90 | 91 | if l: 92 | l = meta(l, ["goto", file_path, highlight_from, line_index, char_index, len(search_for)]) 93 | bbcode.append(l) 94 | 95 | set_bbcode(bbcode.join("\n")) 96 | 97 | 98 | 99 | # get a list of files containging lines 100 | func _search(search_for:String) -> Dictionary: 101 | var out = {} 102 | var search_for_l:String 103 | 104 | if case_toggle.pressed: 105 | search_for_l = search_for 106 | else: 107 | search_for_l = search_for.to_lower() 108 | 109 | var paths:Array 110 | 111 | # search all 112 | if all_toggle.pressed: 113 | paths = editor.file_paths 114 | 115 | # only search selected 116 | else: 117 | var sel = editor.get_selected_file() 118 | if not sel: 119 | var err_msg = "no file open to search" 120 | editor.console.err(err_msg) 121 | push_error(err_msg) 122 | return out 123 | 124 | else: 125 | paths = [sel] 126 | 127 | for path in paths: 128 | var lines = TE_Util.load_text(path).split("\n") 129 | for line_index in len(lines): 130 | var line:String = lines[line_index] 131 | 132 | # make lowercase, if case doesn't matter 133 | if not case_toggle.pressed: 134 | line = line.to_lower() 135 | 136 | # find index where result is found 137 | var char_index:int = line.find(search_for_l) 138 | if char_index != -1: 139 | if not path in out: 140 | out[path] = [] 141 | 142 | var preview_lines = [[true, line_index, lines[line_index]]] 143 | var highlight_from:int = line_index 144 | 145 | # previous few lines before a blank 146 | for i in range(1, 3): 147 | if line_index-i >= 0: 148 | if not lines[line_index-i].strip_edges(): 149 | break 150 | highlight_from = line_index-i 151 | preview_lines.push_front([false, line_index-i, lines[line_index-i]]) 152 | 153 | # next few lines before a blank 154 | for i in range(1, 3): 155 | if line_index+i < len(lines): 156 | if not lines[line_index+i].strip_edges(): 157 | break 158 | preview_lines.push_back([false, line_index+i, lines[line_index+i]]) 159 | 160 | # lines, index in file, index in line 161 | out[path].append([preview_lines, highlight_from, line_index, char_index]) 162 | 163 | return out 164 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # 1.12 2 | - Changed icon. 3 | - Added tabs popup menu: 4 | - Options for closing many tabs. 5 | - Options for selecting tabs. 6 | - Console is hidden on start. 7 | - Fixed `yaml` tabs not working. 8 | - Fixed `tab` + `shift + tab`ing when multiple lines are selected. 9 | 10 | # 1.11 11 | - Toggled `Low Processor Mode` to keep cpu/gpu usage down. 12 | - Simplified *File List* filter display. 13 | - *File List* filter now scans closed folders as well. 14 | - Folder icon in *File List* changes when open/closed. 15 | - *File Editor* now saves states of: 16 | - Cursor position. 17 | - Selection. 18 | - Scroll position. 19 | - Enabled hiding in *File Editor*. 20 | - *Tag List* tags are sorted. 21 | - Added `.rpy` *Renpy* file support. 22 | - Added tab/spaces toggle. 23 | - Fixed files with `.` in their name not showing up. 24 | - Fixed error caused by clicking *File List* symbol for unopened file. 25 | 26 | # 1.10 27 | - Added cursor panel at bottom of Text Editor. 28 | - Word counter. 29 | - Symbol path. 30 | - Added `insert` menu, for inserting Date. 31 | - `ctrl + shift + u` and `ctrl + shift + l` will toggle uppercase and lowercase. 32 | - `ctrl + shift + o` and `ctrl + shift + p` will toggle capitalize and variablize. 33 | - Fixed `ctrl + f` not bringing up search pannel. 34 | - Fixed error when creating new file. 35 | - Removed Text Editor hints. 36 | - Color tweaks. 37 | 38 | # 1.9 39 | - Tag Viewer now shows all tags regardless of whether the file is open or not. 40 | - File View can show symbols. Toggle with `ctrl` click. 41 | - File View filter will scan symbols as well. 42 | - File List dims characters `0123456789-_`. 43 | - Image Preview on `ctrl + click` in Markdown: `![](icon.png)` will display `"res://icon.png"` 44 | - Tab title ignores leading numbers: "001_character" = "character" 45 | - Directories are highlighted if they have a file with a selected tag. 46 | - Holding `ctrl` while selecting a tag allows multiple to be selected. 47 | - Added tab icon based on folder color. 48 | - Fixed Markdown symbol generator including `#` inside code. 49 | - Fixed meta container resizing. 50 | 51 | # 1.8 52 | - Added filter to symbols list. 53 | - Added filter to file list. 54 | - Added `.md` highlighting for `{}`. (Not official Markdown.) 55 | - Fixed unsaved files asking for a save path if no text entered. 56 | - Fixed file wiping if hitting undo after loading a file. 57 | - Fixed *no word_skip_list.txt* error. 58 | - Folders colorized in file list. 59 | - Display version at top right. 60 | 61 | # 1.7 62 | - Added option to view `Extensionless` files. 63 | - Added Symbol path heirarchy to hint popup so you know where you are in big files: 64 | ![](README/changes_hint_toc.png) 65 | - `ctrl + shift +` 66 | - `U` Make selection uppercase. 67 | - `L` Make selection lowercase. 68 | - `O` Make selection capitalized. 69 | - `P` Make selection variable: `My text -> my_text` 70 | - Select file shorctut: 71 | - `ctrl + shift + 0-9` Remember file. 72 | - `ctrl + 0-9` Swap to file. 73 | - Selected Symbol is now highlighted. 74 | - Improved meta data for `.md` files. 75 | - `search` will autoselect term when clicked. 76 | - `search` `all` toggle added to allow only searching in the selected file. 77 | - `search` `case` toggle added to allow searching based on upper/lower case. 78 | - `sys` panel shows unique word list. 79 | - `sys` panel shows time since modified. 80 | - Can create a `word_skip_list.txt` in main folder for ignoring certain words from showing in `sys` word list. 81 | - File List panel hint paths are localized. 82 | - Removed `.md` function color. 83 | - Fixed `trash` not working in exported binaries. 84 | - Fixed dragging files into directory bug. 85 | - Fixed temporary files not closing properly. 86 | - Fixed close non existing tab bug. 87 | - Fixed symbol list not redrawing after file closed. 88 | - Fixed symbol list not redrawing after file type changed. 89 | - Fixed focus not being grabbed when tab selected. 90 | 91 | # 1.6 92 | - Added `Uppercase` `Lowercase` and `Capitalize` option to popup menu for selected text. 93 | - `ctrl + click` in Symbol View selects entire "chapter" and sub "chapters". `ctrl + shift + click` selects only one "chapter". 94 | - `ctrl + click` in editor will auto scroll symbol view. 95 | - Folders can be tinted. 96 | - `word_wrap` state is saved/loaded. 97 | - Fixed error that occured when folder containing binary was moved. 98 | - Markdown can have a `progress` field in meta data which can be sorted in `sys`. 99 | - Markdown meta info taken into account for `sys` 100 | - Markdown meta info colourized. 101 | - Markdown code color based on variable color. 102 | - JSON comments like YAML: `"#": "comment"` 103 | - JSON color tweaks. 104 | 105 | # 1.5 106 | - Added `Ctrl + N` to immediately create new file without defining path. 107 | - New unsaved file will have contents remembered. 108 | - Added `Ctrl + Shift + Mouse wheel` to change ui font size. 109 | - Added word wrap toggle. 110 | - Fixed sorting error in `sys`. 111 | - Fixed font size save/load. 112 | - `sys` shows chapter count. 113 | - Preserves symbol view scroll value when tabbing. 114 | - Can access full filesystem. 115 | - Fixed "New File" dialog not gaining focus. 116 | 117 | # 1.4 118 | - Added `sys` info tab. 119 | - Added `console` info tab. (wip) 120 | - Changing extension updates colors. 121 | - Fixed exported build not styling things properly. 122 | - Fixed symbols/tags not showing when first booting editor. 123 | - Tweaked colors. 124 | - Internal rewriting. 125 | 126 | # 1.3 127 | - Basic search implemented. `Ctrl + F` 128 | - Can create links inside `()` which makes markdown links clickable.: `Ctrl + Click` 129 | 130 | # 1.2 131 | - Can unrecycle now. (Make sure `view/Directories/.trash` is toggled, then press arrow. 132 | - Added folder recycle option 133 | - Added folder move/drag. 134 | - Empty directories properly hide if they have no subdirectories. 135 | - Fixed hide/show file type not updating list. 136 | - Settings are saved more frequently. 137 | - Fixed file dragging. 138 | - Fixed meta table not resizing. 139 | - Tweaked symbol colorizer to emphasize depth. 140 | - Bug fixes. 141 | 142 | # 1.1 143 | - Added `addons` folder hider option. 144 | - Preserve folder open/close state. 145 | - Fixed directories with `.gdignore` not hiding. 146 | - Fixed files and directories not being sorted. 147 | - Fixed "failed to load settings" error. 148 | - Tweaked syntax coloring. 149 | - Got rid of accidental test file. -------------------------------------------------------------------------------- /addons/text_editor/ext/ext_md.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TE_ExtensionHelper 3 | 4 | func generate_meta(t:TextEdit, r:RichTextLabel): 5 | .generate_meta(t, r) 6 | 7 | var i = 0 8 | var meta = {} 9 | var words = {} 10 | var word_count = 0 11 | var chaps = [{i=0, id="???", words={}, word_count=0 }] 12 | while i < t.get_line_count(): 13 | var line = t.get_line(i) 14 | 15 | # get meta 16 | if i == 0 and line.begins_with("---"): 17 | i += 1 18 | while i < t.get_line_count() and not t.get_line(i).begins_with("---"): 19 | if ":" in t.get_line(i): 20 | var p = t.get_line(i).split(":", true, 1) 21 | var k = p[0].strip_edges() 22 | var v = p[1].strip_edges() 23 | meta[k] = v 24 | if k == "name": 25 | chaps[-1].id = v 26 | i += 1 27 | 28 | # ignore comments 29 | elif ""): 122 | return .toggle_comment(t, head, tail) 123 | 124 | func apply_colors(e:TE_Editor, t:TextEdit): 125 | .apply_colors(e, t) 126 | 127 | var code:Color = lerp(Color.white.darkened(.5), Color.deepskyblue, .333) 128 | var quote:Color = lerp(e.color_text, e.color_symbol, .5) 129 | 130 | t.add_color_override("function_color", e.color_text) 131 | t.add_color_override("number_color", e.color_text) 132 | 133 | # t.add_keyword_color("true", e.color_var) 134 | # t.add_keyword_color("false", e.color_var) 135 | 136 | # bold italic 137 | t.add_color_region("***", "***", Color.tomato.darkened(.3), false) 138 | # bold 139 | t.add_color_region("**", "**", Color.tomato, false) 140 | # italic 141 | t.add_color_region("*", "*", Color.tomato.lightened(.3), false) 142 | 143 | # quote 144 | t.add_color_region("> ", "", quote, true) 145 | 146 | # comment 147 | t.add_color_region("", e.color_comment, false) 148 | 149 | # non official markdown: 150 | # formatted 151 | t.add_color_region("{", "}", lerp(e.color_text, e.color_var, .5).darkened(.25), false) 152 | # t.add_color_region("[", "]", lerp(e.color_text, e.color_var, .5).darkened(.25), false) 153 | # t.add_color_region("(", ")", lerp(e.color_text, e.color_var, .5).darkened(.25), false) 154 | if false: 155 | # quote 156 | t.add_color_region('"', '"', quote, false) 157 | # brackets 158 | t.add_color_region('(', ')', quote, false) 159 | else: 160 | # url links 161 | t.add_color_region("![", ")", e.color_var.lightened(.5)) 162 | 163 | # headings 164 | for i in range(1, 7): 165 | var h = "#".repeat(i) 166 | t.add_color_region("%s *" % h, "*", e.get_symbol_color(i-1, -.33), true) 167 | t.add_color_region("%s \"" % h, "\"", e.get_symbol_color(i-1, .33), true) 168 | t.add_color_region("%s " % h, "*", e.get_symbol_color(i-1), true) 169 | 170 | # lists 171 | t.add_color_region("- [x", "]", Color.yellowgreen, false) 172 | t.add_color_region("- [", " ]", e.color_text.darkened(.6), false) 173 | 174 | # code blocks 175 | t.add_color_region("```", "```", code, false) 176 | t.add_color_region("~~~", "~~~", code, false) 177 | t.add_color_region("---", "---", code, false) 178 | 179 | # strikeout 180 | t.add_color_region("~~", "~~", Color.tomato, false) 181 | 182 | # code 183 | t.add_color_region("`", "`", code, false) 184 | 185 | # at/mention 186 | t.add_color_region("@", " ", Color.yellowgreen, false) 187 | 188 | # tables 189 | t.add_color_region("|", "", Color.tan, true) 190 | 191 | func get_symbols(t:String) -> Dictionary: 192 | var out = .get_symbols(t) 193 | var last = add_symbol() 194 | var lines = t.split("\n") 195 | var i = 0 196 | 197 | while i < len(lines): 198 | # initial meta data 199 | if i == 0 and lines[i].begins_with("---"): 200 | i += 1 201 | while i < len(lines) and not lines[i].begins_with("---"): 202 | if "tags: " in lines[i]: 203 | for tag in lines[i].split("tags: ", true, 1)[1].split("#"): 204 | tag = tag.strip_edges() 205 | if tag: 206 | last.tags.append(tag) 207 | 208 | # elif "name: " in lines[i]: 209 | # last.name = lines[i].split("name: ", true, 1)[1] 210 | 211 | i += 1 212 | # i += 1 213 | 214 | elif lines[i].begins_with("```") or lines[i].begins_with("~~~"): 215 | var head = lines[i].substr(0, 3) 216 | i += 1 217 | while i < len(lines) and not lines[i].begins_with(head): 218 | i += 1 219 | 220 | # symbols 221 | elif lines[i].begins_with("#"): 222 | var p = lines[i].split(" ", true, 1) 223 | var deep = len(p[0])-1 224 | var name = "???" if len(p) == 1 else p[1].strip_edges() 225 | last = add_symbol(i, deep, name) 226 | 227 | # tags 228 | elif "", true, 1)[0].split("#"): 230 | tag = tag.strip_edges() 231 | if tag: 232 | last.tags.append(tag) 233 | 234 | i += 1 235 | 236 | return out 237 | -------------------------------------------------------------------------------- /addons/text_editor/TE_ScriptInfo.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends "res://addons/text_editor/TE_RichTextLabel.gd" 3 | 4 | var chapter_info:Array = [] 5 | var sort_on:String = "words" 6 | var sort_on_index:int = 1 7 | var sort_reverse:Dictionary = { id=true, chaps=true, words=true, uwords=true, "%":true, modified=true } 8 | var skip_words:PoolStringArray 9 | 10 | func _ready(): 11 | var btn = get_parent().get_node("update") 12 | btn.add_font_override("font", editor.FONT_R) 13 | 14 | var _e = btn.connect("pressed", self, "_update") 15 | 16 | func _update(): 17 | chapter_info.clear() 18 | 19 | # load block list 20 | var skip_list = editor.current_directory.plus_file("word_skip_list.txt") 21 | if File.new().file_exists(skip_list): 22 | skip_words = TE_Util.load_text(skip_list).replace("\n", " ").strip_edges().to_lower().split(" ") 23 | else: 24 | skip_words = PoolStringArray() 25 | 26 | for path in editor.file_paths: 27 | var file = path.get_file() 28 | var ext = file.get_extension() 29 | match ext: 30 | "md": _process_md(path) 31 | 32 | # clear empty 33 | for i in range(len(chapter_info)-1, -1, -1): 34 | var info = chapter_info[i] 35 | if not info.words: 36 | chapter_info.remove(i) 37 | 38 | _sort() 39 | _redraw() 40 | 41 | const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] 42 | const MONTHS = ["Januaray", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] 43 | 44 | const TIMES:Dictionary = { 45 | "second": 60, 46 | "minute": 60, 47 | "hour": 24, 48 | "day": INF 49 | } 50 | func get_time(t:int) -> String: 51 | for k in TIMES: 52 | if t < TIMES[k]: 53 | return "%s %s ago" % [t, k + ("" if t == 1 else "s")] 54 | t /= TIMES[k] 55 | return "???" 56 | 57 | func _process_md(path:String): 58 | var lines = TE_Util.load_text(path).split("\n") 59 | var file_time = File.new().get_modified_time(path) 60 | var curr_time = OS.get_unix_time() 61 | var diff_time = curr_time - file_time 62 | var time_nice = get_time(diff_time) 63 | 64 | if false and diff_time > 9999999: 65 | time_nice = OS.get_datetime_from_unix_time(file_time) 66 | time_nice.weekday = WEEKDAYS[time_nice.weekday-1].substr(0, 3).to_lower() 67 | time_nice.month = MONTHS[time_nice.month-1].substr(0, 3).to_lower() 68 | time_nice.hour12 = str(time_nice.hour % 12) 69 | time_nice.ampm = "am" if time_nice.hour > 12 else "pm" 70 | time_nice = "{weekday} {month} {day}, {year} {hour12}:{minute}:{second}{ampm}".format(time_nice) 71 | 72 | var out = { path=path, line=0, id=editor.get_localized_path(path), modified=file_time, time_nice=time_nice, words=0, uwords={}, chaps=0, "%":0.0 } 73 | chapter_info.append(out) 74 | var i = 0 75 | while i < len(lines): 76 | # skip head meta 77 | if i == 0 and lines[i].begins_with("---"): 78 | i += 1 79 | while i < len(lines) and not lines[i].begins_with("---"): 80 | if ":" in lines[i]: 81 | var p = lines[i].split(":", true, 1) 82 | var k = p[0].strip_edges() 83 | var v = p[1].strip_edges() 84 | match k: 85 | "name": 86 | out.id = v 87 | 88 | "prog", "progress": 89 | out["%"] = float(v.replace("%", "")) 90 | 91 | i += 1 92 | 93 | # skip comments 94 | elif "