mirror of
https://github.com/Relintai/Godot-TextEditor.git
synced 2025-02-27 12:54:20 +01:00
1.7
This commit is contained in:
parent
4f09def7ef
commit
06e8d176c4
32
CHANGES.md
32
CHANGES.md
@ -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:
|
||||||
|

|
||||||
|
- `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.
|
||||||
|
64
README.md
64
README.md
@ -1,22 +1,23 @@
|
|||||||
# Text Editor
|
# Text Editor
|
||||||
Version `1.6`
|
Version `1.7`
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
***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
BIN
README/changes_hint_toc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
@ -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()
|
||||||
|
|
||||||
|
# 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:
|
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
|
|
||||||
else:
|
"Extensionless":
|
||||||
var text = popup_view_file.get_item_text(index)
|
show.file.extensionless = not show.file.extensionless
|
||||||
var ext = text.substr(2)
|
popup_view_file.set_item_checked(index, show.file.extensionless)
|
||||||
if ext in exts_enabled:
|
|
||||||
exts_enabled[ext] = not exts_enabled[ext]
|
# file extensions
|
||||||
popup_view_file.set_item_checked(index, exts_enabled[ext])
|
_:
|
||||||
else:
|
var ext = text.substr(2)
|
||||||
print("no %s in %s" % [ext, exts_enabled])
|
if ext in 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()
|
|
||||||
|
|
||||||
if not is_opened(file_path):
|
else:
|
||||||
|
var temp = get_temporary_tab()
|
||||||
|
if temp != null:
|
||||||
|
tab_parent.remove_child(temp)
|
||||||
|
temp.queue_free()
|
||||||
|
|
||||||
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()
|
||||||
|
@ -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:
|
||||||
@ -205,6 +258,11 @@ func _input(e):
|
|||||||
for i in len(lines): set_line(f+i, lines[i])
|
for i in len(lines): set_line(f+i, lines[i])
|
||||||
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:
|
||||||
@ -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:
|
||||||
|
@ -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)))
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
elif lines[i].begins_with("progress: "):
|
var v = p[1].strip_edges()
|
||||||
chapter_info[-1]["%"] = float(lines[i].split("progress: ", true, 1)[1].replace("%", ""))
|
match k:
|
||||||
elif lines[i].begins_with("prog: "):
|
"name":
|
||||||
chapter_info[-1]["%"] = float(lines[i].split("prog: ", true, 1)[1].replace("%", ""))
|
out.id = v
|
||||||
|
|
||||||
|
"prog", "progress":
|
||||||
|
out["%"] = float(v.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()
|
||||||
|
@ -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
|
||||||
|
3
addons/text_editor/TE_StopWords.gd
Normal file
3
addons/text_editor/TE_StopWords.gd
Normal file
File diff suppressed because one or more lines are too long
@ -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"))
|
||||||
|
@ -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):
|
||||||
|
@ -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 )
|
||||||
|
@ -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]
|
||||||
|
@ -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()
|
||||||
|
@ -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 )
|
||||||
|
@ -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 )
|
||||||
|
@ -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 )
|
||||||
|
@ -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 )
|
||||||
|
@ -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 )
|
||||||
|
@ -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
12
dummy.gd
Normal 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
1
word_skip_list.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
ctrl file files sys symbol
|
Loading…
Reference in New Issue
Block a user