This commit is contained in:
teebarjunk 2021-10-24 11:27:27 -04:00
parent 4f09def7ef
commit 06e8d176c4
24 changed files with 746 additions and 180 deletions

View File

@ -1,6 +1,36 @@
# 1.7
- Added option to view `Extensionless` files.
- Added Symbol path heirarchy to hint popup so you know where you are in big files:
![](README/changes_hint_toc.png)
- `ctrl + shift +`
- `U` Make selection uppercase.
- `L` Make selection lowercase.
- `O` Make selection capitalized.
- `P` Make selection variable: `My text -> my_text`
- Select file shorctut:
- `ctrl + shift + 0-9` Remember file.
- `ctrl + 0-9` Swap to file.
- Selected Symbol is now highlighted.
- Improved meta data for `.md` files.
- `search` will autoselect term when clicked.
- `search` `all` toggle added to allow only searching in the selected file.
- `search` `case` toggle added to allow searching based on upper/lower case.
- `sys` panel shows unique word list.
- `sys` panel shows time since modified.
- Can create a `word_skip_list.txt` in main folder for ignoring certain words from showing in `sys` word list.
- File List panel hint paths are localized.
- Removed `.md` function color.
- Fixed `trash` not working in exported binaries.
- Fixed dragging files into directory bug.
- Fixed temporary files not closing properly.
- Fixed close non existing tab bug.
- Fixed symbol list not redrawing after file closed.
- Fixed symbol list not redrawing after file type changed.
- Fixed focus not being grabbed when tab selected.
# 1.6 # 1.6
- Added `Uppercase` `Lowercase` and `Capitalize` option to popup menu for selected text. - Added `Uppercase` `Lowercase` and `Capitalize` option to popup menu for selected text.
- `ctrl + click` in symbol view selects entire "chapter" and sub "chapters". `ctrl + shift + click` selects only one "chapter". - `ctrl + click` in Symbol View selects entire "chapter" and sub "chapters". `ctrl + shift + click` selects only one "chapter".
- `ctrl + click` in editor will auto scroll symbol view. - `ctrl + click` in editor will auto scroll symbol view.
- Folders can be tinted. - Folders can be tinted.
- `word_wrap` state is saved/loaded. - `word_wrap` state is saved/loaded.

View File

@ -1,22 +1,23 @@
# Text Editor # Text Editor
Version `1.6` Version `1.7`
![](README/readme_preview.png) ![](README/readme_preview.png)
***Warning: Use at your own risk. Backup your files before testing.*** ***Warning: Use at your own risk. Backup your files before testing.***
# Features # Features
- Tabs with scroll - Multi file tab system.
- File filtering - File browser filtering.
- Highlighting for common file formats (`md` `json`...) - Highlighting for common formats (`md` `json` `ini`...)
- Tag filtering system - Tag system.
- File Management: - File Management:
- Creation - Creation.
- Renaming - Renaming.
- Recycling - Recycling.
- Moving.
- Search files.
- Auto save/load settings - Auto save/load settings
- Many little *Ease of life* functions: - Many little *Ease of Life* functions:
- Folder open/close
- Comment toggling for: - Comment toggling for:
- `.md`: `<!-- -->` - `.md`: `<!-- -->`
- `.json`: `/* */` - `.json`: `/* */`
@ -25,17 +26,19 @@ Version `1.6`
- `.yaml`: `# ` - `.yaml`: `# `
# Controls # Controls
- `ctrl + N` New file - `ctrl + N` New file.
- `ctrl + W` Close file - `ctrl + W` Close file.
- `ctrl + shift + W` Open last closed file - `ctrl + shift + W` Open last closed file .
- `ctrl + tab` Select next open file - `ctrl + tab` Select next open file.
- `ctrl + shift + tab` Select last open file - `ctrl + shift + tab` Select last open file.
- `ctrl + mouse wheel` Adjust font size - `ctrl + mouse wheel` Adjust font size.
- `ctrl + shift + mouse wheel` Adjust ui font size - `ctrl + shift + mouse wheel` Adjust ui font size.
- `ctrl + up` `ctrl + down` Move selected lines - `ctrl + up` & `ctrl + down` Move selected lines.
- `ctrl + /` Toggle line comments - `ctrl + /` Toggle line comments.
- `ctrl + M` Toggle file meta info - `ctrl + M` Toggle file meta info.
- `ctrl + F` Search for text in all files - `ctrl + F` Search for text in all files.
- `ctrl + shift + 0-9` Create hotkey for selected file.
- `ctrl + 0-9` Load hotkeyed file.
## Symbol View ## Symbol View
- `ctrl + click` Select entire block + children. - `ctrl + click` Select entire block + children.
@ -44,9 +47,14 @@ Version `1.6`
## Editor View ## Editor View
- `ctrl + click` anywhere: Scroll to nearest symbol in symbol view. - `ctrl + click` anywhere: Scroll to nearest symbol in symbol view.
- `ctrl + click` inside brackets: Goto local file. - `ctrl + click` inside brackets: Goto local file.
- `ctrl + shift +`
- `U` Make selection uppercase.
- `L` Make selection lowercase.
- `O` Make selection capitalized.
- `P` Make selection variable: `My text -> my_text`
# Symbols and Tags # Symbols and Tags
*Symbols* are like a Table of Contents for a file. *Symbols* are like *Table of Contents* for a file.
- `Markdown` uses headings `# Heading` - `Markdown` uses headings `# Heading`
- `JSON` uses Dictionaries `"object": {` - `JSON` uses Dictionaries `"object": {`
@ -70,10 +78,14 @@ This will then highlight *Files* and *Symbols* that have that tag.
# Todo # Todo
- [x] `1.1` Preserve folders open/close state. - [x] `1.1` Preserve folders open/close state.
- [x] `1.3` Search all files. - [x] `1.3` Search all files.
- [ ] Search file. - [x] `1.7` Search file.
- [ ] Find and replace. - [ ] Find and replace.
- [ ] Improve meta data based on format. - [x] `1.7` Improve meta data based on format.
- [x] `1.2` Recycle folders. - [x] `1.2` Recycle folders.
- [x] `1.2` Unrecylce. (Toggle `view/directories/.trash` and press green arrow. - [x] `1.2` Unrecylce. (Toggle `view/directories/.trash` and press green arrow.)
- [ ] JSON formatting. - [ ] JSON formatting.
- [ ] JSON error testing. - [ ] JSON error testing.
- [ ] Color themes.
# Mini features
- Clicking in file will scroll symbol view to the nearest symbol, and display it's name in the tool tip hint.

BIN
README/changes_hint_toc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -9,8 +9,8 @@ const FONT_B:DynamicFont = preload("res://addons/text_editor/fonts/font_b.tres")
const FONT_I:DynamicFont = preload("res://addons/text_editor/fonts/font_i.tres") const FONT_I:DynamicFont = preload("res://addons/text_editor/fonts/font_i.tres")
const FONT_BI:DynamicFont = preload("res://addons/text_editor/fonts/font_bi.tres") const FONT_BI:DynamicFont = preload("res://addons/text_editor/fonts/font_bi.tres")
const PATH_TRASH:String = "res://.trash" const DNAME_TRASH:String = ".trash"
const PATH_STATE:String = "res://.text_editor_state.json" const FNAME_STATE:String = ".text_editor_state.json"
const MAIN_EXTENSIONS:PoolStringArray = PoolStringArray([ const MAIN_EXTENSIONS:PoolStringArray = PoolStringArray([
"txt", "md", "json", "csv", "cfg", "ini", "yaml" "txt", "md", "json", "csv", "cfg", "ini", "yaml"
@ -57,11 +57,13 @@ var show:Dictionary = {
trash=false trash=false
}, },
file={ file={
hidden=false hidden=false,
extensionless=false
} }
} }
var color_text:Color = Color.white var color_text:Color = Color.white
var color_background:Color = Color.transparent#Color.white.darkened(.85)
var color_comment:Color = Color.white.darkened(.6) var color_comment:Color = Color.white.darkened(.6)
var color_symbol:Color = Color.deepskyblue var color_symbol:Color = Color.deepskyblue
var color_tag:Color = Color.yellow var color_tag:Color = Color.yellow
@ -161,7 +163,8 @@ func _ready():
popup_view_file.clear() popup_view_file.clear()
popup_view_file.set_name("Files") popup_view_file.set_name("Files")
popup_view_file.add_font_override("font", FONT_R) popup_view_file.add_font_override("font", FONT_R)
popup_view_file.add_check_item("Hidden", 0) popup_view_file.add_check_item("Hidden")
popup_view_file.add_check_item("Extensionless")
popup_view_file.set_item_checked(0, show.file.hidden) popup_view_file.set_item_checked(0, show.file.hidden)
popup_view_file.add_separator() popup_view_file.add_separator()
@ -233,7 +236,10 @@ func update_checks():
func get_localized_path(file_path:String): func get_localized_path(file_path:String):
assert(file_path.begins_with(current_directory)) assert(file_path.begins_with(current_directory))
return file_path.substr(len(current_directory)) var out:String = file_path.substr(len(current_directory))
if out.begins_with("/"):
return out.substr(1)
return out
func get_globalized_path(file_path:String): func get_globalized_path(file_path:String):
return current_directory.plus_file(file_path) return current_directory.plus_file(file_path)
@ -244,13 +250,14 @@ func save_state():
"font_size": FONT.size, "font_size": FONT.size,
"font_size_ui": FONT_R.size, "font_size_ui": FONT_R.size,
"tabs": {}, "tabs": {},
"selected": get_localized_path(get_selected_file()), "selected": get_selected_file(),
"word_wrap": word_wrap.pressed, "word_wrap": word_wrap.pressed,
"show": show, "show": show,
"tags": tags, "tags": tags,
"tag_counts": tag_counts, "tag_counts": tag_counts,
"tags_enabled": tags_enabled, "tags_enabled": tags_enabled,
"exts_enabled": exts_enabled, "exts_enabled": exts_enabled,
"shortcuts": shortcuts,
"file_list": file_list, "file_list": file_list,
@ -265,7 +272,7 @@ func save_state():
for tab in get_all_tabs(): for tab in get_all_tabs():
state.tabs[get_localized_path(tab.file_path)] = tab.get_state() state.tabs[get_localized_path(tab.file_path)] = tab.get_state()
TE_Util.save_json(PATH_STATE, state) TE_Util.save_json(current_directory.plus_file(FNAME_STATE), state)
emit_signal("state_saved") emit_signal("state_saved")
func _fix_tint(d:Dictionary): func _fix_tint(d:Dictionary):
@ -274,7 +281,7 @@ func _fix_tint(d:Dictionary):
d.tint = Color(c[0], c[1], c[2], c[3]) d.tint = Color(c[0], c[1], c[2], c[3])
func load_state(): func load_state():
var state:Dictionary = TE_Util.load_json(PATH_STATE) var state:Dictionary = TE_Util.load_json(current_directory.plus_file(FNAME_STATE))
if not state: if not state:
return return
@ -283,14 +290,14 @@ func load_state():
word_wrap.pressed = ww word_wrap.pressed = ww
set_word_wrap(ww) set_word_wrap(ww)
var selected var selected_file:String
for file_path in state.tabs: for file_path in state.tabs:
var st = state.tabs[file_path] var st = state.tabs[file_path]
file_path = get_globalized_path(file_path) file_path = get_globalized_path(file_path)
var tab = _open_file(file_path) var tab = _open_file(file_path)
tab.set_state(st) tab.set_state(st)
if file_path == state.selected: if file_path == state.selected:
selected = tab selected_file = file_path
_load_property(state, "show", true) _load_property(state, "show", true)
@ -308,6 +315,7 @@ func load_state():
_load_property(state, "tag_counts") _load_property(state, "tag_counts")
_load_property(state, "tags_enabled") _load_property(state, "tags_enabled")
_load_property(state, "exts_enabled") _load_property(state, "exts_enabled")
_load_property(state, "shortcuts")
# dividers # dividers
$c/div1.split_offset = state.get("div1", $c/div1.split_offset) $c/div1.split_offset = state.get("div1", $c/div1.split_offset)
@ -323,8 +331,10 @@ func load_state():
emit_signal("state_loaded") emit_signal("state_loaded")
yield(get_tree(), "idle_frame") yield(get_tree(), "idle_frame")
if selected: if selected_file:
emit_signal("file_selected", selected.file_path) select_file(selected_file)
# if selected_tab:
# emit_signal("file_selected", selected_tab.file_path)
func _load_property(state:Dictionary, property:String, merge:bool=false): func _load_property(state:Dictionary, property:String, merge:bool=false):
if property in state and typeof(state[property]) == typeof(self[property]): if property in state and typeof(state[property]) == typeof(self[property]):
@ -351,6 +361,7 @@ func is_plugin_active():
return plugin_hint and visible return plugin_hint and visible
var shortcuts:Dictionary = {}
func _input(e): func _input(e):
if not is_plugin_active(): if not is_plugin_active():
return return
@ -374,7 +385,10 @@ func _input(e):
if e.shift: if e.shift:
open_last_file() open_last_file()
else: else:
get_selected_tab().close() var sel_tab = get_selected_tab()
if sel_tab != null:
sel_tab.close()
get_tree().set_input_as_handled() get_tree().set_input_as_handled()
# create new file # create new file
@ -382,8 +396,23 @@ func _input(e):
open_file("", true) open_file("", true)
get_tree().set_input_as_handled() get_tree().set_input_as_handled()
if e is InputEventMouseButton and e.control: # shortcuts
elif e.scancode >= KEY_0 and e.scancode <= KEY_9:
if e.shift:
var sf = get_selected_file()
if sf:
shortcuts[str(e.scancode)] = sf
console.msg("shortcut %s: %s" % [e.scancode, sf])
else:
if str(e.scancode) in shortcuts:
var sf = shortcuts[str(e.scancode)]
open_file(sf)
select_file(sf)
get_tree().set_input_as_handled()
if e is InputEventMouseButton and e.control:
# ui font # ui font
if e.shift: if e.shift:
if e.button_index == BUTTON_WHEEL_DOWN: if e.button_index == BUTTON_WHEEL_DOWN:
@ -447,20 +476,24 @@ func _menu_view_dir(index:int):
save_state() save_state()
func _menu_view_file(index:int): func _menu_view_file(index:int):
# hidden files var text = popup_view_file.get_item_text(index)
if index == 0: match text:
show.file.hidden = not show.file.hidden "Hidden":
popup_view_file.set_item_checked(index, show.file.hidden) show.file.hidden = not show.file.hidden
popup_view_file.set_item_checked(index, show.file.hidden)
# main extensions "Extensionless":
else: show.file.extensionless = not show.file.extensionless
var text = popup_view_file.get_item_text(index) popup_view_file.set_item_checked(index, show.file.extensionless)
var ext = text.substr(2)
if ext in exts_enabled: # file extensions
exts_enabled[ext] = not exts_enabled[ext] _:
popup_view_file.set_item_checked(index, exts_enabled[ext]) var ext = text.substr(2)
else: if ext in exts_enabled:
print("no %s in %s" % [ext, exts_enabled]) exts_enabled[ext] = not exts_enabled[ext]
popup_view_file.set_item_checked(index, exts_enabled[ext])
else:
print("no %s in %s" % [ext, exts_enabled])
refresh_files() refresh_files()
save_state() save_state()
@ -643,7 +676,11 @@ func _open_file(file_path:String):
return tab return tab
func is_allowed_extension(file_path:String) -> bool: func is_allowed_extension(file_path:String) -> bool:
var ext = get_extension(file_path) var file = file_path.get_file()
if not "." in file and show.file.extensionless:
return true
var ext = get_extension(file)
return ext in MAIN_EXTENSIONS return ext in MAIN_EXTENSIONS
func open_file(file_path:String, temporary:bool=false): func open_file(file_path:String, temporary:bool=false):
@ -695,8 +732,18 @@ func unrecycle(file_path:String):
push_error(err_msg) push_error(err_msg)
console.err(err_msg) console.err(err_msg)
func recycle(file_path:String): func is_trash_path(file_path:String) -> bool:
if file_path.begins_with(PATH_TRASH): var path_trash:String = current_directory.plus_file(DNAME_TRASH)
return file_path.begins_with(path_trash) and file_path != path_trash
func recycle(file_path:String, is_file:bool):
if not is_file:
print("TODO: close all open windows")
var path_trash:String = current_directory.plus_file(DNAME_TRASH)
if file_path.begins_with(path_trash):
var err_msg = "can't recycle recycled %s" % file_path var err_msg = "can't recycle recycled %s" % file_path
push_error(err_msg) push_error(err_msg)
console.err(err_msg) console.err(err_msg)
@ -710,14 +757,25 @@ func recycle(file_path:String):
# is dir? # is dir?
var base_name = file_path.get_file() var base_name = file_path.get_file()
var new_dir = PATH_TRASH.plus_file(time) var new_dir = path_trash.plus_file(time)
var new_path = new_dir.plus_file(base_name) var new_path = new_dir.plus_file(base_name)
if not d.dir_exists(PATH_TRASH): if not d.dir_exists(path_trash):
var _err = d.make_dir(PATH_TRASH) var err = d.make_dir(path_trash)
if err != OK:
err("can't make dir %s" % path_trash)
return
var err = d.make_dir(new_dir)
if err != OK:
err("can't make dir %s" % new_dir)
return
err = d.rename(file_path, new_path)
if err != OK:
err("can't rename %s to %s" % [file_path, new_path])
return
var _err = d.make_dir(new_dir)
d.rename(file_path, new_path)
save_file(new_dir.plus_file(".old_path"), old_path) save_file(new_dir.plus_file(".old_path"), old_path)
save_file(new_dir.plus_file(".new_path"), new_path) save_file(new_dir.plus_file(".new_path"), new_path)
@ -730,6 +788,10 @@ func recycle(file_path:String):
if opened: if opened:
select_file(opened[-1]) select_file(opened[-1])
func err(err_msg:String):
push_error(err_msg)
console.err(err_msg)
func rename_file(old_path:String, new_path:String): func rename_file(old_path:String, new_path:String):
if old_path == new_path or not old_path or not new_path: if old_path == new_path or not old_path or not new_path:
return return
@ -740,12 +802,12 @@ func rename_file(old_path:String, new_path:String):
console.err(err_msg) console.err(err_msg)
return return
var selected = get_selected_file() var was_selected = old_path == get_selected_file()
if Directory.new().rename(old_path, new_path) == OK: if Directory.new().rename(old_path, new_path) == OK:
refresh_files() refresh_files()
if selected == old_path:
_selected_file_changed(new_path)
emit_signal("file_renamed", old_path, new_path) emit_signal("file_renamed", old_path, new_path)
if was_selected:
_selected_file_changed(new_path)
else: else:
var err_msg = "couldn't rename %s to %s." % [old_path, new_path] var err_msg = "couldn't rename %s to %s." % [old_path, new_path]
@ -760,14 +822,17 @@ func select_file(file_path:String):
if not is_allowed_extension(file_path): if not is_allowed_extension(file_path):
return return
var temp = get_temporary_tab() if is_opened(file_path):
if temp: var tab = get_tab(file_path)
if temp.file_path == file_path: if tab.temporary:
temp.temporary = false tab.temporary = false
else:
temp.close() else:
var temp = get_temporary_tab()
if temp != null:
tab_parent.remove_child(temp)
temp.queue_free()
if not is_opened(file_path):
open_file(file_path, true) open_file(file_path, true)
# select current tab # select current tab
@ -829,8 +894,12 @@ func show_dir(fname:String, base_dir:String) -> bool:
return true return true
func show_file(fname:String) -> bool: func show_file(fname:String) -> bool:
# hidden
if fname.begins_with("."): if fname.begins_with("."):
if not show.file.hidden: return false if not show.file.hidden: return false
# extensionless
if not "." in fname:
return show.file.extensionless
var ext = get_extension(fname) var ext = get_extension(fname)
return exts_enabled.get(ext, false) return exts_enabled.get(ext, false)
@ -908,11 +977,17 @@ static func get_extension(file_path:String) -> String:
return file.split(".", true, 1)[1] return file.split(".", true, 1)[1]
return "" return ""
var complained_ext:Array = []
func get_extension_helper(file_path:String) -> TE_ExtensionHelper: func get_extension_helper(file_path:String) -> TE_ExtensionHelper:
var ext:String = get_extension(file_path).replace(".", "_") var ext:String = get_extension(file_path).replace(".", "_")
var ext_path:String = "res://addons/text_editor/ext/ext_%s.gd" % ext var ext_path:String = "res://addons/text_editor/ext/ext_%s.gd" % ext
if ext in ["cfg", "csv", "ini", "json", "md", "yaml"]: if ext in ["cfg", "csv", "ini", "json", "md", "yaml"]:
return load(ext_path).new() return load(ext_path).new()
console.err("no helper %s" % ext_path) # only complain once
if not ext in complained_ext:
complained_ext.append(ext)
console.err("no format helper for '%s' files" % ext)
return load("res://addons/text_editor/ext/TE_ExtensionHelper.gd").new() return load("res://addons/text_editor/ext/TE_ExtensionHelper.gd").new()

View File

@ -50,10 +50,18 @@ func _ready():
popup.add_font_override("font", editor.FONT) popup.add_font_override("font", editor.FONT)
popup.add_separator() popup.add_separator()
popup.add_item("Uppercase") popup.add_item("Uppercase", 1000)
# var sc = ShortCut.new()
# sc.shortcut = InputEventKey.new()
# sc.shortcut.shift = true
# sc.shortcut.control = true
# sc.shortcut.scancode = KEY_U
# popup.add_item_shortcut(sc, 1000)
popup.add_item("Lowercase") popup.add_item("Lowercase")
popup.add_item("Capitalize") popup.add_item("Capitalize")
popup.add_item("Variable") popup.add_item("Variable")
# popup.add_shortcut()
_e = popup.connect("index_pressed", self, "_popup_menu") _e = popup.connect("index_pressed", self, "_popup_menu")
# hint # hint
@ -69,17 +77,49 @@ func _popup_menu(index:int):
"Capitalize": selection_capitalize() "Capitalize": selection_capitalize()
"Variable": selection_variable() "Variable": selection_variable()
var cl
var cc
var isa
var sl1
var sc1
var sl2
var sc2
func _remember_selection():
cl = cursor_get_line()
cc = cursor_get_column()
isa = is_selection_active()
if isa:
sl1 = get_selection_from_line()
sc1 = get_selection_from_column()
sl2 = get_selection_to_line()
sc2 = get_selection_to_column()
func _remake_selection():
cursor_set_line(cl)
cursor_set_column(cc)
if isa:
select(sl1, sc1, sl2, sc2)
func selection_uppercase(): func selection_uppercase():
_remember_selection()
insert_text_at_cursor(get_selection_text().to_upper()) insert_text_at_cursor(get_selection_text().to_upper())
_remake_selection()
func selection_lowercase(): func selection_lowercase():
_remember_selection()
insert_text_at_cursor(get_selection_text().to_lower()) insert_text_at_cursor(get_selection_text().to_lower())
_remake_selection()
func selection_variable(): func selection_variable():
_remember_selection()
insert_text_at_cursor(get_selection_text().to_lower().replace(" ", "_")) insert_text_at_cursor(get_selection_text().to_lower().replace(" ", "_"))
_remake_selection()
func selection_capitalize(): func selection_capitalize():
_remember_selection()
insert_text_at_cursor(get_selection_text().capitalize()) insert_text_at_cursor(get_selection_text().capitalize())
_remake_selection()
func _node(n): func _node(n):
var _e var _e
@ -131,6 +171,25 @@ func _file_renamed(old_path:String, new_path:String):
update_name() update_name()
update_colors() update_colors()
func _update_selected_line():
var l = cursor_get_line()
editor.select_symbol_line(0)
var depth = PoolStringArray()
for i in len(symbols):
var sindex = clamp(i, 0, len(symbols))
var symbol = symbols.values()[sindex]
while len(depth) <= symbol.deep:
depth.append("")
depth[symbol.deep] = " ".repeat(symbol.deep) + symbol.name
if i == len(symbols)-1 or symbols.keys()[i+1] > l:
editor.select_symbol_line(sindex)
depth.resize(symbol.deep+1)
hint_tooltip = "[%s]\n%s" % [editor.get_localized_path(file_path), depth.join("\n")]
break
func _input(e): func _input(e):
if not editor.is_plugin_active(): if not editor.is_plugin_active():
return return
@ -138,20 +197,14 @@ func _input(e):
if not visible or not in_focus or not mouse_inside: if not visible or not in_focus or not mouse_inside:
return return
# show current position in heirarchy as editor hint
if e is InputEventMouseButton and not e.pressed:
_update_selected_line()
if e is InputEventMouseButton and not e.pressed and e.control: if e is InputEventMouseButton and not e.pressed and e.control:
var line:String = get_line(cursor_get_line()) var line:String = get_line(cursor_get_line())
# if selecting a symbol, show it in symbol viewer # click link
if cursor_get_line() in symbols:
editor.select_symbol_line(symbols.keys().find(cursor_get_line())-1)
else:
var l = cursor_get_line()
editor.select_symbol_line(0)
for i in len(symbols):
if symbols.keys()[i] > l:
editor.select_symbol_line(max(0, i-2))
break
var ca = line.find("(") var ca = line.find("(")
var cb = line.find_last(")") var cb = line.find_last(")")
if ca != -1 and cb != -1: if ca != -1 and cb != -1:
@ -206,6 +259,11 @@ func _input(e):
select(f+1, 0, t+1, len(get_line(t+1))) select(f+1, 0, t+1, len(get_line(t+1)))
cursor_set_line(cursor_get_line()+1, false) cursor_set_line(cursor_get_line()+1, false)
if e.scancode == KEY_U: selection_uppercase()
if e.scancode == KEY_L: selection_lowercase()
if e.scancode == KEY_O: selection_capitalize()
if e.scancode == KEY_P: selection_variable()
func _unhandled_key_input(e): func _unhandled_key_input(e):
if not visible: if not visible:
return return
@ -249,11 +307,15 @@ func _file_selected(p:String):
if had_selection: if had_selection:
select(fl, fc, tl, tc) select(fl, fc, tl, tc)
grab_focus()
grab_click_focus()
func goto_line(line:int): func goto_line(line:int):
# force scroll to bottom so selected line will be at top # force scroll to bottom so selected line will be at top
cursor_set_line(get_line_count()) cursor_set_line(get_line_count())
cursor_set_line(line) cursor_set_line(line)
_update_selected_line()
func text_changed(): func text_changed():
if last_selected: if last_selected:

View File

@ -56,8 +56,12 @@ func _dir_popup(index:int):
file = file.file_path file = file.file_path
match dir_popup.get_item_text(index): match dir_popup.get_item_text(index):
"New File": editor.popup_create_file(file) "New File":
"Remove": editor.recycle(file) editor.popup_create_file(file)
"Remove":
editor.recycle(file, type == "f")
"Tint Yellow": "Tint Yellow":
selected[1].tint = Color.gold selected[1].tint = Color.gold
_redraw() _redraw()
@ -89,7 +93,7 @@ func _file_popup(index:int):
"Remove": "Remove":
if type == "f": if type == "f":
editor.recycle(file) editor.recycle(file, true)
_: _:
selected = [] selected = []
@ -119,9 +123,11 @@ func _input(e:InputEvent):
if type in ["f", "d"]: if type in ["f", "d"]:
var file_path = file if type == "f" else file.file_path var file_path = file if type == "f" else file.file_path
if file_path.begins_with(editor.PATH_TRASH): # can't move recycling
return # can't move recycling if editor.is_trash_path(file_path):
return
# select for drag
else: else:
dragging = meta_hovered dragging = meta_hovered
@ -134,9 +140,10 @@ func _input(e:InputEvent):
var drag_type = dragging[0] var drag_type = dragging[0]
var drag_file = dragging[1] var drag_file = dragging[1]
# dragged onto directory?
if type == "d": if type == "d":
var dir:String = file.file_path var dir:String = file.file_path
var old_path:String = drag_file.file_path var old_path:String = drag_file if drag_type == "f" else drag_file.file_path
var new_path:String = dir.plus_file(old_path.get_file()) var new_path:String = dir.plus_file(old_path.get_file())
editor.rename_file(old_path, new_path) editor.rename_file(old_path, new_path)
@ -151,7 +158,7 @@ func _input(e:InputEvent):
# unrecycle # unrecycle
"unrecycle": "unrecycle":
editor.unrecycle(file) editor.unrecycle(file.file_path)
# select # select
"f": "f":
@ -194,8 +201,8 @@ func _draw_dir(dir:Dictionary, deep:int):
var head:String = "" if dir.open else "" var head:String = "" if dir.open else ""
head = clr(space+FOLDER+head, Color.white.darkened(.5)) head = clr(space+FOLDER+head, Color.white.darkened(.5))
head += " " + b(file.get_file()) head += " " + b(file.get_file())
var link:String = meta(head, ["d", dir], file) var link:String = meta(head, ["d", dir], editor.get_localized_path(file))
if file.begins_with(editor.PATH_TRASH) and file.count("/") == 3: if editor.is_trash_path(file):
link += " " + meta(clr("", Color.yellowgreen), ["unrecycle", dir], file) link += " " + meta(clr("", Color.yellowgreen), ["unrecycle", dir], file)
lines.append(clr(link, dir.tint.darkened(dimmest))) lines.append(clr(link, dir.tint.darkened(dimmest)))
@ -214,7 +221,7 @@ func _draw_dir(dir:Dictionary, deep:int):
for i in len(dir.files): for i in len(dir.files):
var file_path = dir.files[i] var file_path = dir.files[i]
file = file_path.get_file() file = file_path.get_file()
var p = file.split(".", true, 1) var p = [file, ""] if not "." in file else file.split(".", true, 1)
file = p[0] file = p[0]
var ext = p[1] var ext = p[1]
@ -226,11 +233,11 @@ func _draw_dir(dir:Dictionary, deep:int):
if "readme" in file.to_lower(): if "readme" in file.to_lower():
head = "🛈" head = "🛈"
if is_selected: # if is_selected or is_opened:
head = "" # head = "● "
#
elif is_opened: # elif is_opened:
head = "" # head = "○ "
head = clr(head, Color.white.darkened(.5 if is_opened else .75)) head = clr(head, Color.white.darkened(.5 if is_opened else .75))
@ -244,6 +251,14 @@ func _draw_dir(dir:Dictionary, deep:int):
pass pass
file = clr(file, color) file = clr(file, color)
ext = clr("." + ext, Color.white.darkened(.65)) ext = "" if not ext else clr("." + ext, Color.white.darkened(.65))
var line = space + head + file + ext
lines.append(meta(line, ["f", file_path], file_path)) var line = file + ext
if is_selected:
line = u(line)
# if is_opened:
# line = b(line)
lines.append(meta(space + head + line, ["f", file_path], editor.get_localized_path(file_path)))

View File

@ -17,6 +17,9 @@ func _file_saved(_file_path:String):
_redraw() _redraw()
func _redraw(): func _redraw():
if not visible:
return
var tab = editor.get_selected_tab() var tab = editor.get_selected_tab()
if tab: if tab:
tab.helper.generate_meta(tab, self) tab.helper.generate_meta(tab, self)

View File

@ -97,6 +97,7 @@ func table(rows) -> String:
func b(t:String) -> String: return "[b]%s[/b]" % t func b(t:String) -> String: return "[b]%s[/b]" % t
func i(t:String) -> String: return "[i]%s[/i]" % t func i(t:String) -> String: return "[i]%s[/i]" % t
func u(t:String) -> String: return "[u]%s[/u]" % t
func clr(t:String, c:Color) -> String: return "[color=#%s]%s[/color]" % [c.to_html(), t] func clr(t:String, c:Color) -> String: return "[color=#%s]%s[/color]" % [c.to_html(), t]
func center(t:String): return "[center]%s[/center]" % t func center(t:String): return "[center]%s[/center]" % t

View File

@ -3,7 +3,9 @@ extends "res://addons/text_editor/TE_RichTextLabel.gd"
var chapter_info:Array = [] var chapter_info:Array = []
var sort_on:String = "words" var sort_on:String = "words"
var sort_reverse:Dictionary = { id=false, words=false, chaps=false, "%":false } var sort_on_index:int = 1
var sort_reverse:Dictionary = { id=true, chaps=true, words=true, uwords=true, "%":true, modified=true }
var skip_words:PoolStringArray
func _ready(): func _ready():
var btn = get_parent().get_node("update") var btn = get_parent().get_node("update")
@ -14,6 +16,10 @@ func _ready():
func _update(): func _update():
chapter_info.clear() chapter_info.clear()
# load block list
var skip_list = editor.current_directory.plus_file("word_skip_list.txt")
skip_words = TE_Util.load_text(skip_list).replace("\n", " ").strip_edges().to_lower().split(" ")
for path in editor.file_paths: for path in editor.file_paths:
var file = path.get_file() var file = path.get_file()
var ext = file.get_extension() var ext = file.get_extension()
@ -29,33 +35,62 @@ func _update():
_sort() _sort()
_redraw() _redraw()
func _chapter(path:String, line:int, id:String): const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
if not id: const MONTHS = ["Januaray", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
id = "???"
chapter_info.append({ path=path, line=line, id=id, words=0, chaps=0, "%":0.0 }) const TIMES:Dictionary = {
"second": 60,
"minute": 60,
"hour": 24,
"day": INF
}
func get_time(t:int) -> String:
for k in TIMES:
if t < TIMES[k]:
return "%s %s ago" % [t, k + ("" if t == 1 else "s")]
t /= TIMES[k]
return "???"
func _process_md(path:String): func _process_md(path:String):
var lines = TE_Util.load_text(path).split("\n") var lines = TE_Util.load_text(path).split("\n")
var is_entire_file:bool = false var file_time = File.new().get_modified_time(path)
var curr_time = OS.get_unix_time()
var diff_time = curr_time - file_time
var time_nice = get_time(diff_time)
_chapter(path, 0, "(Noname)") if false and diff_time > 9999999:
time_nice = OS.get_datetime_from_unix_time(file_time)
time_nice.weekday = WEEKDAYS[time_nice.weekday-1].substr(0, 3).to_lower()
time_nice.month = MONTHS[time_nice.month-1].substr(0, 3).to_lower()
time_nice.hour12 = str(time_nice.hour % 12)
time_nice.ampm = "am" if time_nice.hour > 12 else "pm"
time_nice = "{weekday} {month} {day}, {year} {hour12}:{minute}:{second}{ampm}".format(time_nice)
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 }
chapter_info.append(out)
var i = 0 var i = 0
while i < len(lines): while i < len(lines):
# skip head meta # skip head meta
if i == 0 and lines[i].begins_with("---"): if i == 0 and lines[i].begins_with("---"):
is_entire_file = true
i += 1 i += 1
while i < len(lines) and not lines[i].begins_with("---"): while i < len(lines) and not lines[i].begins_with("---"):
if lines[i].begins_with("name: "): if ":" in lines[i]:
chapter_info[-1].id = lines[i].split("name: ", true, 1)[1] var p = lines[i].split(":", true, 1)
var k = p[0].strip_edges()
var v = p[1].strip_edges()
match k:
"name":
out.id = v
elif lines[i].begins_with("progress: "): "prog", "progress":
chapter_info[-1]["%"] = float(lines[i].split("progress: ", true, 1)[1].replace("%", "")) out["%"] = float(v.replace("%", ""))
elif lines[i].begins_with("prog: "):
chapter_info[-1]["%"] = float(lines[i].split("prog: ", true, 1)[1].replace("%", ""))
i += 1 i += 1
# skip comments
elif "<!--" in lines[i]:
pass
# skip code blocks # skip code blocks
elif lines[i].begins_with("~~~") or lines[i].begins_with("```"): elif lines[i].begins_with("~~~") or lines[i].begins_with("```"):
var head = lines[i].substr(0, 3) var head = lines[i].substr(0, 3)
@ -69,23 +104,37 @@ func _process_md(path:String):
var id = lines[i].split(" ", true, 1) var id = lines[i].split(" ", true, 1)
var deep = len(id[0]) var deep = len(id[0])
id = "???" if len(id) == 1 else id[1].strip_edges() id = "???" if len(id) == 1 else id[1].strip_edges()
if deep == 1 and not is_entire_file: out.chaps += 1
_chapter(path, i, id)
else:
chapter_info[-1].chaps += 1
else: else:
var words = lines[i].split(" ", false) out.words += TE_Util.count_words(lines[i], out.uwords, skip_words)
chapter_info[-1].words += len(words)
i += 1 i += 1
# sort word counts
TE_Util.sort_vals(out.uwords)
var words = PoolStringArray(out.uwords.keys())
var words_top = words
if len(words_top) > 16:
words_top.resize(16)
out.uwords = words_top.join(" ")
var word_lines = [""]
for word in words:
if len(word_lines[-1]) >= 64:
word_lines.append("")
if word_lines[-1]:
word_lines[-1] += " "
word_lines[-1] += word
out.uwords_all = PoolStringArray(word_lines).join("\n")
func _clicked(args): func _clicked(args):
match args[0]: match args[0]:
"sort_table": "sort_table":
var key = args[1] var key = args[1]
if sort_on != key: if sort_on != key:
sort_on = key sort_on = key
sort_on_index = sort_reverse.keys().find(sort_on)
else: else:
sort_reverse[key] = not sort_reverse[key] sort_reverse[key] = not sort_reverse[key]
@ -114,9 +163,14 @@ func _redraw():
var c1 = Color.white.darkened(.4) var c1 = Color.white.darkened(.4)
var c2 = Color.white.darkened(.3) var c2 = Color.white.darkened(.3)
var cols = ["id", "words", "chaps", "%"] var ch1 = lerp(c1, Color.yellowgreen, .5)
var ch2 = lerp(c2, Color.yellowgreen, .5)
var cols = ["id", "chaps", "words", "uwords", "%", "modified"]
push_align(RichTextLabel.ALIGN_CENTER) push_align(RichTextLabel.ALIGN_CENTER)
push_table(len(cols)) push_table(len(cols))
add_constant_override("table_hseparation", 8)
for id in cols: for id in cols:
push_cell() push_cell()
push_bold() push_bold()
@ -137,30 +191,51 @@ func _redraw():
for i in len(chapter_info): for i in len(chapter_info):
var item = chapter_info[i] var item = chapter_info[i]
var clr = c1 if i%2==0 else c2 var clr = c1 if i%2==0 else c2
var clrh = ch1 if i%2==0 else ch2
# id # id
push_cell() push_cell()
push_color(clr) push_color(clrh if sort_on_index == 0 else clr)
push_meta(add_meta(["goto", item.path, item.line], item.path)) push_meta(add_meta(["goto", item.path, item.line], item.path + "\n" + item.uwords_all))
add_text(item.id) add_text(item.id)
pop() pop()
pop() pop()
pop() pop()
# chapters
push_cell()
push_color(clrh if sort_on_index == 1 else clr)
add_text(TE_Util.commas(item.chaps))
pop()
pop()
# word cound # word cound
for x in ["words", "chaps"]: push_cell()
push_cell() push_color(clrh if sort_on_index == 2 else clr)
push_color(clr) add_text(TE_Util.commas(item.words))
add_text(TE_Util.commas(item[x])) pop()
pop() pop()
pop()
# unique words
push_cell()
push_color(clrh if sort_on_index == 3 else clr)
add_text(item.uwords)
pop()
pop()
# percent # percent
push_cell() push_cell()
push_color(clr) push_color(clrh if sort_on_index == 4 else clr)
add_text(str(int(item["%"]))) add_text(str(int(item["%"])))
pop() pop()
pop() pop()
# time
push_cell()
push_color(clrh if sort_on_index == 5 else clr)
add_text(item.time_nice)
pop()
pop()
pop() pop()
pop() pop()

View File

@ -1,7 +1,9 @@
tool tool
extends "res://addons/text_editor/TE_RichTextLabel.gd" extends "res://addons/text_editor/TE_RichTextLabel.gd"
onready var line_edit:LineEdit = get_parent().get_node("le") onready var line_edit:LineEdit = get_parent().get_node("c/le")
onready var all_toggle:CheckBox = get_parent().get_node("c/all")
onready var case_toggle:CheckBox = get_parent().get_node("c/case")
var console_urls:Array = [] var console_urls:Array = []
var last_search:String = "" var last_search:String = ""
@ -10,7 +12,10 @@ func _ready():
var _e var _e
_e = line_edit.connect("text_entered", self, "_text_entered") _e = line_edit.connect("text_entered", self, "_text_entered")
# fix fonts
line_edit.add_font_override("font", editor.FONT_R) line_edit.add_font_override("font", editor.FONT_R)
all_toggle.add_font_override("font", editor.FONT_R)
case_toggle.add_font_override("font", editor.FONT_R)
func _unhandled_key_input(e): func _unhandled_key_input(e):
if not editor.is_plugin_active(): if not editor.is_plugin_active():
@ -20,16 +25,26 @@ func _unhandled_key_input(e):
if e.scancode == KEY_F and e.pressed and e.control: if e.scancode == KEY_F and e.pressed and e.control:
line_edit.grab_focus() line_edit.grab_focus()
line_edit.grab_click_focus() line_edit.grab_click_focus()
line_edit.select_all()
# show meta tab
get_parent().get_parent().show() get_parent().get_parent().show()
# make search tab current meta tab
get_parent().get_parent().current_tab = get_parent().get_index() get_parent().get_parent().current_tab = get_parent().get_index()
get_tree().set_input_as_handled() get_tree().set_input_as_handled()
func _clicked(args): func _clicked(args):
match args[0]: match args[0]:
"goto": "goto":
var tab = editor.open_file(args[1]) var tab:TextEdit = editor.open_file(args[1])
editor.select_file(args[1]) editor.select_file(args[1])
tab.goto_line(int(args[2])) yield(get_tree(), "idle_frame")
# goto line
var line = int(args[2])
tab.goto_line(line)
# select area
var from = int(args[3])
var lenn = int(args[4])
tab.select(line, from, line, from + lenn)
func _text_entered(search_for:String): func _text_entered(search_for:String):
last_search = search_for last_search = search_for
@ -44,7 +59,7 @@ func _text_entered(search_for:String):
var file_path:String = fpaths[k] var file_path:String = fpaths[k]
var l = clr("%s/%s " % [k+1, len(fpaths)], Color.orange) + clr(file_path.get_file(), Color.yellow) var l = clr("%s/%s " % [k+1, len(fpaths)], Color.orange) + clr(file_path.get_file(), Color.yellow)
l = meta(l, ["goto", file_path, 0], file_path) l = meta(l, ["goto", file_path, 0, 0, 0], file_path)
bbcode.append(l) bbcode.append(l)
for j in len(found[file_path]): for j in len(found[file_path]):
@ -62,43 +77,76 @@ func _text_entered(search_for:String):
if i == 2: if i == 2:
var line:String = got_lines[i] var line:String = got_lines[i]
var head:String = line.substr(0, char_index) var head:String = line.substr(0, char_index)
var midd:String = line.substr(char_index, len(search_for)+1) var midd:String = line.substr(char_index, len(search_for))
var tail:String = line.substr(char_index+len(search_for)+1) var tail:String = line.substr(char_index+len(search_for))
head = clr(head, Color.tomato.lightened(.5)) head = clr(head, Color.deepskyblue.lightened(.5))
midd = clr(midd, Color.tomato.darkened(.25)) midd = clr(midd, Color.deepskyblue.darkened(.25))
tail = clr(tail, Color.tomato.lightened(.5)) tail = clr(tail, Color.deepskyblue.lightened(.5))
l = "\t" + clr(str(line_index-2+i+1) + ": ", Color.tomato.lightened(.5)) + (head+midd+tail) l = "\t" + clr(str(line_index-2+i+1) + ": ", Color.deepskyblue.lightened(.5)) + (head+midd+tail)
elif line_index-2+i >= 0: elif line_index-2+i >= 0:
l = "\t" + clr(str(line_index-2+i+1) + ": ", Color.gray) + got_lines[i] l = "\t" + clr(str(line_index-2+i+1) + ": ", Color.gray) + got_lines[i]
if l: if l:
l = meta(l, ["goto", file_path, line_index]) l = meta(l, ["goto", file_path, line_index, char_index, len(search_for)])
bbcode.append(l) bbcode.append(l)
set_bbcode(bbcode.join("\n")) set_bbcode(bbcode.join("\n"))
# get a list of files containging lines # get a list of files containging lines
func _search(search_for:String) -> Dictionary: func _search(search_for:String) -> Dictionary:
var found = {} var out = {}
var search_for_l = search_for.to_lower() # lowercase. TODO: match case var search_for_l:String
for path in editor.file_paths:
if case_toggle.pressed:
search_for_l = search_for
else:
search_for_l = search_for.to_lower()
var paths:Array
# search all
if all_toggle.pressed:
paths = editor.file_paths
# only search selected
else:
var sel = editor.get_selected_file()
if not sel:
var err_msg = "no file open to search"
editor.console.err(err_msg)
push_error(err_msg)
return out
else:
paths = [sel]
for path in paths:
var lines = TE_Util.load_text(path).split("\n") var lines = TE_Util.load_text(path).split("\n")
for line_index in len(lines): for line_index in len(lines):
var line:String = lines[line_index].to_lower() var line:String = lines[line_index]
# make lowercase, if case doesn't matter
if not case_toggle.pressed:
line = line.to_lower()
# find index where result is found # find index where result is found
var char_index:int = line.find(search_for_l) var char_index:int = line.find(search_for_l)
if char_index != -1: if char_index != -1:
if not path in found: if not path in out:
found[path] = [] out[path] = []
var preview_lines = PoolStringArray() var preview_lines = PoolStringArray()
var highlight_from:int = line_index var highlight_from:int = line_index
# show surrounding 5 lines. # show surrounding 5 lines.
for i in range(-2, 3): for i in range(-2, 3):
if line_index+i >= 0 and line_index+i < len(lines): if line_index+i >= 0 and line_index+i < len(lines):
preview_lines.append(lines[line_index+i]) preview_lines.append(lines[line_index+i])
else: else:
preview_lines.append("") preview_lines.append("")
# lines, index in file, index in line # lines, index in file, index in line
found[path].append([preview_lines, line_index, char_index]) out[path].append([preview_lines, line_index, char_index])
return found
return out

File diff suppressed because one or more lines are too long

View File

@ -2,12 +2,16 @@ tool
extends "res://addons/text_editor/TE_RichTextLabel.gd" extends "res://addons/text_editor/TE_RichTextLabel.gd"
var hscrolls:Dictionary = {} var hscrolls:Dictionary = {}
var selected_line:int = 0
var current_file:String = ""
func _ready(): func _ready():
var _e var _e
_e = editor.connect("symbols_updated", self, "_redraw") _e = editor.connect("symbols_updated", self, "_redraw")
_e = editor.connect("tags_updated", self, "_redraw") _e = editor.connect("tags_updated", self, "_redraw")
_e = editor.connect("file_selected", self, "_file_selected") _e = editor.connect("file_selected", self, "_file_selected")
_e = editor.connect("file_closed", self, "_file_closed")
_e = editor.connect("file_renamed", self, "_file_renamed")
_e = editor.connect("selected_symbol_line", self, "_selected_symbol_line") _e = editor.connect("selected_symbol_line", self, "_selected_symbol_line")
_e = get_v_scroll().connect("value_changed", self, "_scrolling") _e = get_v_scroll().connect("value_changed", self, "_scrolling")
@ -19,13 +23,25 @@ func _ready():
call_deferred("_redraw") call_deferred("_redraw")
func _selected_symbol_line(line:int): func _selected_symbol_line(line:int):
# scroll_to_line(get_line_count()-1) selected_line = clamp(line, 0, get_line_count())
scroll_to_line(line) scroll_to_line(clamp(line-1, 0, get_line_count()-1))
_redraw()
func _file_selected(file_path:String): func _file_selected(file_path:String):
current_file = file_path
yield(get_tree(), "idle_frame") yield(get_tree(), "idle_frame")
get_v_scroll().value = hscrolls.get(file_path, 0) get_v_scroll().value = hscrolls.get(file_path, 0)
func _file_renamed(old:String, new:String):
current_file = new
yield(get_tree(), "idle_frame")
_redraw()
func _file_closed(file_path:String):
if file_path == current_file:
current_file = ""
_redraw()
func _scrolling(v): func _scrolling(v):
hscrolls[editor.get_selected_file()] = get_v_scroll().value hscrolls[editor.get_selected_file()] = get_v_scroll().value
@ -79,8 +95,9 @@ func _redraw():
else: else:
var t = PoolStringArray() var t = PoolStringArray()
var i = -1
for line_index in symbols: for line_index in symbols:
i += 1
if line_index == -1: if line_index == -1:
continue # special file chapter continue # special file chapter
var symbol_info = symbols[line_index] var symbol_info = symbols[line_index]
@ -102,6 +119,10 @@ func _redraw():
cl = cl.darkened(.33 * (deep-1)) cl = cl.darkened(.33 * (deep-1))
var tags = "" if not symbol_info.tags else PoolStringArray(symbol_info.tags).join(", ") var tags = "" if not symbol_info.tags else PoolStringArray(symbol_info.tags).join(", ")
t.append(clr(meta(space + symbol_info.name, [symbol_info, line_index], tags), cl)) var text = clr(meta(space + symbol_info.name, [symbol_info, line_index], tags), cl)
if i == selected_line:
text = b(u(text))
t.append(text)
set_bbcode(t.join("\n")) set_bbcode(t.join("\n"))

View File

@ -1,5 +1,74 @@
class_name TE_Util class_name TE_Util
class Sorter:
var d:Dictionary
var a:Array = []
func _init(dict:Dictionary):
d = dict
for k in d:
a.append([k, d[k]])
func on_keys(reverse:bool=false):
a.sort_custom(self, "_sort_keys_rev" if reverse else "_sort_keys")
return _out()
func on_vals(reverse:bool=false):
a.sort_custom(self, "_sort_vals_rev" if reverse else "_sort_vals")
return _out()
func _sort_keys(a, b): return a[0] > b[0]
func _sort_keys_rev(a, b): return a[0] < b[0]
func _sort_vals(a, b): return a[1] > b[1]
func _sort_vals_rev(a, b): return a[1] < b[1]
func _out() -> Dictionary:
d.clear()
for item in a:
d[item[0]] = item[1]
return d
static func sort_keys(d:Dictionary, reverse:bool=false): return Sorter.new(d).on_keys(reverse)
static func sort_vals(d:Dictionary, reverse:bool=false): return Sorter.new(d).on_vals(reverse)
static func count_words(text:String, counter:Dictionary, skip_words=null):
var word_count:int = 0
for sentence in text.split("."):
for word in sentence.split(" "):
word = _sanitize_word(word)
if not word: continue
if word in TE_StopWords.STOP_WORDS: continue
if skip_words and word in skip_words: continue
word_count += 1
if not word in counter:
counter[word] = 1
else:
counter[word] += 1
return word_count
static func _sanitize_word(word:String):
var out = ""
var has_letter = false
for c in word.to_lower():
if c in "abcdefghijklmnopqrstuvwxyz":
out += c
has_letter = true
elif c in "-'0123456789":
out += c
if not has_letter:
return ""
if out.ends_with("'s"):
return out.substr(0, len(out)-2)
return out
static func load_text(path:String) -> String: static func load_text(path:String) -> String:
var f:File = File.new() var f:File = File.new()
if f.file_exists(path): if f.file_exists(path):

View File

@ -206,7 +206,7 @@ split_offset = -200
[node name="tab_container" type="TabContainer" parent="c/div1/div2/c/c"] [node name="tab_container" type="TabContainer" parent="c/div1/div2/c/c"]
margin_right = 614.0 margin_right = 614.0
margin_bottom = 562.0 margin_bottom = 283.0
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
custom_fonts/font = ExtResource( 12 ) custom_fonts/font = ExtResource( 12 )
@ -218,13 +218,13 @@ __meta__ = {
} }
[node name="meta_tabs" type="TabContainer" parent="c/div1/div2/c/c"] [node name="meta_tabs" type="TabContainer" parent="c/div1/div2/c/c"]
visible = false margin_top = 295.0
margin_top = 288.0
margin_right = 614.0 margin_right = 614.0
margin_bottom = 566.0 margin_bottom = 562.0
script = ExtResource( 16 ) script = ExtResource( 16 )
[node name="console" type="RichTextLabel" parent="c/div1/div2/c/c/meta_tabs"] [node name="console" type="RichTextLabel" parent="c/div1/div2/c/c/meta_tabs"]
visible = false
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
margin_left = 4.0 margin_left = 4.0
@ -265,7 +265,6 @@ bbcode_enabled = true
script = ExtResource( 9 ) script = ExtResource( 9 )
[node name="search" type="VBoxContainer" parent="c/div1/div2/c/c/meta_tabs"] [node name="search" type="VBoxContainer" parent="c/div1/div2/c/c/meta_tabs"]
visible = false
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
margin_left = 4.0 margin_left = 4.0
@ -275,16 +274,33 @@ margin_bottom = -4.0
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
[node name="le" type="LineEdit" parent="c/div1/div2/c/c/meta_tabs/search"] [node name="c" type="HBoxContainer" parent="c/div1/div2/c/c/meta_tabs/search"]
margin_right = 58.0 margin_right = 606.0
margin_bottom = 24.0 margin_bottom = 27.0
[node name="le" type="LineEdit" parent="c/div1/div2/c/c/meta_tabs/search/c"]
margin_right = 423.0
margin_bottom = 27.0
size_flags_horizontal = 3 size_flags_horizontal = 3
custom_fonts/font = ExtResource( 12 ) custom_fonts/font = ExtResource( 12 )
[node name="all" type="CheckBox" parent="c/div1/div2/c/c/meta_tabs/search/c"]
margin_left = 427.0
margin_right = 501.0
margin_bottom = 27.0
pressed = true
text = "all files"
[node name="case" type="CheckBox" parent="c/div1/div2/c/c/meta_tabs/search/c"]
margin_left = 505.0
margin_right = 606.0
margin_bottom = 27.0
text = "match case"
[node name="rte" type="RichTextLabel" parent="c/div1/div2/c/c/meta_tabs/search"] [node name="rte" type="RichTextLabel" parent="c/div1/div2/c/c/meta_tabs/search"]
margin_top = 28.0 margin_top = 31.0
margin_right = 58.0 margin_right = 606.0
margin_bottom = 28.0 margin_bottom = 231.0
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
theme = SubResource( 4 ) theme = SubResource( 4 )
@ -306,17 +322,16 @@ margin_right = -4.0
margin_bottom = -4.0 margin_bottom = -4.0
[node name="update" type="Button" parent="c/div1/div2/c/c/meta_tabs/sys"] [node name="update" type="Button" parent="c/div1/div2/c/c/meta_tabs/sys"]
margin_right = 12.0 margin_right = 606.0
margin_bottom = 20.0 margin_bottom = 28.0
size_flags_horizontal = 3 size_flags_horizontal = 3
custom_fonts/font = ExtResource( 12 ) custom_fonts/font = ExtResource( 12 )
text = "⟳" text = "⟳"
[node name="sys" type="RichTextLabel" parent="c/div1/div2/c/c/meta_tabs/sys"] [node name="sys" type="RichTextLabel" parent="c/div1/div2/c/c/meta_tabs/sys"]
anchor_right = 1.0 margin_top = 32.0
anchor_bottom = 1.0 margin_right = 606.0
margin_right = -8.0 margin_bottom = 232.0
margin_bottom = -36.0
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 3 size_flags_vertical = 3
theme = SubResource( 5 ) theme = SubResource( 5 )

View File

@ -9,7 +9,7 @@ func generate_meta(t:TextEdit, r:RichTextLabel):
var words = TE_Util.commas(len(t.text.split(" ", false))) var words = TE_Util.commas(len(t.text.split(" ", false)))
var lines = TE_Util.commas(len(TE_Util.split_many(t.text, ".?!\n", false))) var lines = TE_Util.commas(len(TE_Util.split_many(t.text, ".?!\n", false)))
var bytes = TE_Util.file_size(t.file_path) var bytes = TE_Util.file_size(t.file_path)
# r.add_constant_override("table_hseparation", int(r.rect_size.x / 5.0))
r.set_bbcode(r.table([ r.set_bbcode(r.table([
["chars", "words", "lines", "bytes"], ["chars", "words", "lines", "bytes"],
[chars, words, lines, bytes] [chars, words, lines, bytes]

View File

@ -1,12 +1,132 @@
tool tool
extends TE_ExtensionHelper extends TE_ExtensionHelper
func generate_meta(t:TextEdit, r:RichTextLabel):
.generate_meta(t, r)
var i = 0
var meta = {}
var words = {}
var word_count = 0
var chaps = [{i=0, id="???", words={}, word_count=0 }]
while i < t.get_line_count():
var line = t.get_line(i)
# get meta
if i == 0 and line.begins_with("---"):
i += 1
while i < t.get_line_count() and not t.get_line(i).begins_with("---"):
if ":" in t.get_line(i):
var p = t.get_line(i).split(":", true, 1)
var k = p[0].strip_edges()
var v = p[1].strip_edges()
meta[k] = v
if k == "name":
chaps[-1].id = v
i += 1
# ignore comments
elif "<!--" in line:
pass
# ignore tables
elif "|" in line:
pass
# ignore code
elif line.begins_with("```") or line.begins_with("~~~"):
var head = line.substr(0, 3)
i += 1
while i < t.get_line_count() and not t.get_line(i).begins_with(head):
i += 1
# get chapter info
elif line.begins_with("#"):
var id = line.split(" ", true, 1)[1].strip_edges()
chaps.append({i=i, id=id, words={}, word_count=0 })
else:
var last = chaps[-1]
last.word_count += TE_Util.count_words(line, last.words)
i += 1
# total words
for chap in chaps:
word_count += chap.word_count
for word in chap.words:
if not word in words:
words[word] = chap.words[word]
else:
words[word] += chap.words[word]
# sort
TE_Util.sort_vals(chap.words)
r.push_align(RichTextLabel.ALIGN_CENTER)
r.push_table(4)
for x in ["#", "id", "word %s" % word_count, "words"]:
r.push_cell()
r.push_bold()
r.add_text(x)
r.pop()
r.pop()
var index:int = 0
for chap in chaps:
if chap.id == "???" and not chap.word_count:
continue
index += 1
r.push_cell()
r.push_color(Color.webgray)
r.add_text(str(index))
r.pop()
r.pop()
r.push_cell()
r.push_color(Color.webgray)
r.add_text(chap.id)
r.pop()
r.pop()
var div = 0 if not chap.word_count or not word_count else chap.word_count / float(word_count)
div *= 100.0
div = "%" + str(stepify(div, .1))
r.push_cell()
r.push_color(Color.webgray)
r.add_text(str(chap.word_count))
r.pop()
r.push_color(Color.gray)
r.add_text(" %s" % div)
r.pop()
r.pop()
r.push_cell()
r.push_color(Color.webgray)
r.add_text(PoolStringArray(chap.words.keys()).join(" "))
r.pop()
r.pop()
r.pop()
r.pop()
func _sort(a, b):
return a[1] > b[1]
func toggle_comment(t:TextEdit, head:String="<!-- ", tail:String=" -->"): func toggle_comment(t:TextEdit, head:String="<!-- ", tail:String=" -->"):
return .toggle_comment(t, head, tail) return .toggle_comment(t, head, tail)
func apply_colors(e:TE_Editor, t:TextEdit): func apply_colors(e:TE_Editor, t:TextEdit):
.apply_colors(e, t) .apply_colors(e, t)
t.add_color_override("function_color", e.color_text)
# t.add_color_override("background_color", e.color_background)
t.add_keyword_color("true", e.color_var) t.add_keyword_color("true", e.color_var)
t.add_keyword_color("false", e.color_var) t.add_keyword_color("false", e.color_var)
@ -59,7 +179,6 @@ func apply_colors(e:TE_Editor, t:TextEdit):
# tables # tables
t.add_color_region("|", "", Color.tan, true) t.add_color_region("|", "", Color.tan, true)
func get_symbols(t:String) -> Dictionary: func get_symbols(t:String) -> Dictionary:
var out = .get_symbols(t) var out = .get_symbols(t)
var last = add_symbol() var last = add_symbol()

View File

@ -5,6 +5,7 @@
[ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3]
[resource] [resource]
size = 12
use_filter = true use_filter = true
font_data = ExtResource( 1 ) font_data = ExtResource( 1 )
fallback/0 = ExtResource( 3 ) fallback/0 = ExtResource( 3 )

View File

@ -5,6 +5,7 @@
[ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3]
[resource] [resource]
size = 12
use_filter = true use_filter = true
font_data = ExtResource( 1 ) font_data = ExtResource( 1 )
fallback/0 = ExtResource( 3 ) fallback/0 = ExtResource( 3 )

View File

@ -5,6 +5,7 @@
[ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3]
[resource] [resource]
size = 12
use_filter = true use_filter = true
font_data = ExtResource( 1 ) font_data = ExtResource( 1 )
fallback/0 = ExtResource( 3 ) fallback/0 = ExtResource( 3 )

View File

@ -5,6 +5,7 @@
[ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3]
[resource] [resource]
size = 12
use_filter = true use_filter = true
font_data = ExtResource( 1 ) font_data = ExtResource( 1 )
fallback/0 = ExtResource( 3 ) fallback/0 = ExtResource( 3 )

View File

@ -5,6 +5,7 @@
[ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3] [ext_resource path="res://addons/text_editor/fonts/unifont-13.0.01.ttf" type="DynamicFontData" id=3]
[resource] [resource]
size = 12
use_filter = true use_filter = true
font_data = ExtResource( 1 ) font_data = ExtResource( 1 )
fallback/0 = ExtResource( 3 ) fallback/0 = ExtResource( 3 )

View File

@ -3,5 +3,5 @@
name="TextEditor" name="TextEditor"
description="A text editor for Godot." description="A text editor for Godot."
author="teebar" author="teebar"
version="1.6" version="1.7"
script="plugin.gd" script="plugin.gd"

12
dummy.gd Normal file
View File

@ -0,0 +1,12 @@
tool
extends Node2D
export(String, MULTILINE) var text:String
func _draw():
var words = PoolStringArray()
for word in text.split("\n"):
word = TE_Util._sanitize_word(word)
if word:
words.append("\"%s\"" % word)
print("[%s]" % words.join(","))

1
word_skip_list.txt Normal file
View File

@ -0,0 +1 @@
ctrl file files sys symbol