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
|
||||
- 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.
|
||||
- Folders can be tinted.
|
||||
- `word_wrap` state is saved/loaded.
|
||||
|
62
README.md
62
README.md
@ -1,22 +1,23 @@
|
||||
# Text Editor
|
||||
Version `1.6`
|
||||
Version `1.7`
|
||||
|
||||

|
||||
|
||||
***Warning: Use at your own risk. Backup your files before testing.***
|
||||
|
||||
# Features
|
||||
- Tabs with scroll
|
||||
- File filtering
|
||||
- Highlighting for common file formats (`md` `json`...)
|
||||
- Tag filtering system
|
||||
- Multi file tab system.
|
||||
- File browser filtering.
|
||||
- Highlighting for common formats (`md` `json` `ini`...)
|
||||
- Tag system.
|
||||
- File Management:
|
||||
- Creation
|
||||
- Renaming
|
||||
- Recycling
|
||||
- Creation.
|
||||
- Renaming.
|
||||
- Recycling.
|
||||
- Moving.
|
||||
- Search files.
|
||||
- Auto save/load settings
|
||||
- Many little *Ease of life* functions:
|
||||
- Folder open/close
|
||||
- Many little *Ease of Life* functions:
|
||||
- Comment toggling for:
|
||||
- `.md`: `<!-- -->`
|
||||
- `.json`: `/* */`
|
||||
@ -25,17 +26,19 @@ Version `1.6`
|
||||
- `.yaml`: `# `
|
||||
|
||||
# Controls
|
||||
- `ctrl + N` New file
|
||||
- `ctrl + W` Close file
|
||||
- `ctrl + shift + W` Open last closed file
|
||||
- `ctrl + tab` Select next open file
|
||||
- `ctrl + shift + tab` Select last open file
|
||||
- `ctrl + mouse wheel` Adjust font size
|
||||
- `ctrl + shift + mouse wheel` Adjust ui font size
|
||||
- `ctrl + up` `ctrl + down` Move selected lines
|
||||
- `ctrl + /` Toggle line comments
|
||||
- `ctrl + M` Toggle file meta info
|
||||
- `ctrl + F` Search for text in all files
|
||||
- `ctrl + N` New file.
|
||||
- `ctrl + W` Close file.
|
||||
- `ctrl + shift + W` Open last closed file .
|
||||
- `ctrl + tab` Select next open file.
|
||||
- `ctrl + shift + tab` Select last open file.
|
||||
- `ctrl + mouse wheel` Adjust font size.
|
||||
- `ctrl + shift + mouse wheel` Adjust ui font size.
|
||||
- `ctrl + up` & `ctrl + down` Move selected lines.
|
||||
- `ctrl + /` Toggle line comments.
|
||||
- `ctrl + M` Toggle file meta info.
|
||||
- `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
|
||||
- `ctrl + click` Select entire block + children.
|
||||
@ -44,9 +47,14 @@ Version `1.6`
|
||||
## Editor View
|
||||
- `ctrl + click` anywhere: Scroll to nearest symbol in symbol view.
|
||||
- `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* are like a Table of Contents for a file.
|
||||
*Symbols* are like *Table of Contents* for a file.
|
||||
|
||||
- `Markdown` uses headings `# Heading`
|
||||
- `JSON` uses Dictionaries `"object": {`
|
||||
@ -70,10 +78,14 @@ This will then highlight *Files* and *Symbols* that have that tag.
|
||||
# Todo
|
||||
- [x] `1.1` Preserve folders open/close state.
|
||||
- [x] `1.3` Search all files.
|
||||
- [ ] Search file.
|
||||
- [x] `1.7` Search file.
|
||||
- [ ] 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` Unrecylce. (Toggle `view/directories/.trash` and press green arrow.
|
||||
- [x] `1.2` Unrecylce. (Toggle `view/directories/.trash` and press green arrow.)
|
||||
- [ ] JSON formatting.
|
||||
- [ ] 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_BI:DynamicFont = preload("res://addons/text_editor/fonts/font_bi.tres")
|
||||
|
||||
const PATH_TRASH:String = "res://.trash"
|
||||
const PATH_STATE:String = "res://.text_editor_state.json"
|
||||
const DNAME_TRASH:String = ".trash"
|
||||
const FNAME_STATE:String = ".text_editor_state.json"
|
||||
|
||||
const MAIN_EXTENSIONS:PoolStringArray = PoolStringArray([
|
||||
"txt", "md", "json", "csv", "cfg", "ini", "yaml"
|
||||
@ -57,11 +57,13 @@ var show:Dictionary = {
|
||||
trash=false
|
||||
},
|
||||
file={
|
||||
hidden=false
|
||||
hidden=false,
|
||||
extensionless=false
|
||||
}
|
||||
}
|
||||
|
||||
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_symbol:Color = Color.deepskyblue
|
||||
var color_tag:Color = Color.yellow
|
||||
@ -161,7 +163,8 @@ func _ready():
|
||||
popup_view_file.clear()
|
||||
popup_view_file.set_name("Files")
|
||||
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.add_separator()
|
||||
@ -233,7 +236,10 @@ func update_checks():
|
||||
|
||||
func get_localized_path(file_path:String):
|
||||
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):
|
||||
return current_directory.plus_file(file_path)
|
||||
@ -244,13 +250,14 @@ func save_state():
|
||||
"font_size": FONT.size,
|
||||
"font_size_ui": FONT_R.size,
|
||||
"tabs": {},
|
||||
"selected": get_localized_path(get_selected_file()),
|
||||
"selected": get_selected_file(),
|
||||
"word_wrap": word_wrap.pressed,
|
||||
"show": show,
|
||||
"tags": tags,
|
||||
"tag_counts": tag_counts,
|
||||
"tags_enabled": tags_enabled,
|
||||
"exts_enabled": exts_enabled,
|
||||
"shortcuts": shortcuts,
|
||||
|
||||
"file_list": file_list,
|
||||
|
||||
@ -265,7 +272,7 @@ func save_state():
|
||||
for tab in get_all_tabs():
|
||||
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")
|
||||
|
||||
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])
|
||||
|
||||
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:
|
||||
return
|
||||
|
||||
@ -283,14 +290,14 @@ func load_state():
|
||||
word_wrap.pressed = ww
|
||||
set_word_wrap(ww)
|
||||
|
||||
var selected
|
||||
var selected_file:String
|
||||
for file_path in state.tabs:
|
||||
var st = state.tabs[file_path]
|
||||
file_path = get_globalized_path(file_path)
|
||||
var tab = _open_file(file_path)
|
||||
tab.set_state(st)
|
||||
if file_path == state.selected:
|
||||
selected = tab
|
||||
selected_file = file_path
|
||||
|
||||
_load_property(state, "show", true)
|
||||
|
||||
@ -308,6 +315,7 @@ func load_state():
|
||||
_load_property(state, "tag_counts")
|
||||
_load_property(state, "tags_enabled")
|
||||
_load_property(state, "exts_enabled")
|
||||
_load_property(state, "shortcuts")
|
||||
|
||||
# dividers
|
||||
$c/div1.split_offset = state.get("div1", $c/div1.split_offset)
|
||||
@ -323,8 +331,10 @@ func load_state():
|
||||
emit_signal("state_loaded")
|
||||
|
||||
yield(get_tree(), "idle_frame")
|
||||
if selected:
|
||||
emit_signal("file_selected", selected.file_path)
|
||||
if selected_file:
|
||||
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):
|
||||
if property in state and typeof(state[property]) == typeof(self[property]):
|
||||
@ -351,6 +361,7 @@ func is_plugin_active():
|
||||
|
||||
return plugin_hint and visible
|
||||
|
||||
var shortcuts:Dictionary = {}
|
||||
func _input(e):
|
||||
if not is_plugin_active():
|
||||
return
|
||||
@ -374,7 +385,10 @@ func _input(e):
|
||||
if e.shift:
|
||||
open_last_file()
|
||||
else:
|
||||
get_selected_tab().close()
|
||||
var sel_tab = get_selected_tab()
|
||||
if sel_tab != null:
|
||||
sel_tab.close()
|
||||
|
||||
get_tree().set_input_as_handled()
|
||||
|
||||
# create new file
|
||||
@ -382,8 +396,23 @@ func _input(e):
|
||||
open_file("", true)
|
||||
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
|
||||
if e.shift:
|
||||
if e.button_index == BUTTON_WHEEL_DOWN:
|
||||
@ -447,14 +476,18 @@ func _menu_view_dir(index:int):
|
||||
save_state()
|
||||
|
||||
func _menu_view_file(index:int):
|
||||
# hidden files
|
||||
if index == 0:
|
||||
var text = popup_view_file.get_item_text(index)
|
||||
match text:
|
||||
"Hidden":
|
||||
show.file.hidden = not show.file.hidden
|
||||
popup_view_file.set_item_checked(index, show.file.hidden)
|
||||
|
||||
# main extensions
|
||||
else:
|
||||
var text = popup_view_file.get_item_text(index)
|
||||
"Extensionless":
|
||||
show.file.extensionless = not show.file.extensionless
|
||||
popup_view_file.set_item_checked(index, show.file.extensionless)
|
||||
|
||||
# file extensions
|
||||
_:
|
||||
var ext = text.substr(2)
|
||||
if ext in exts_enabled:
|
||||
exts_enabled[ext] = not exts_enabled[ext]
|
||||
@ -643,7 +676,11 @@ func _open_file(file_path:String):
|
||||
return tab
|
||||
|
||||
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
|
||||
|
||||
func open_file(file_path:String, temporary:bool=false):
|
||||
@ -695,8 +732,18 @@ func unrecycle(file_path:String):
|
||||
push_error(err_msg)
|
||||
console.err(err_msg)
|
||||
|
||||
func recycle(file_path:String):
|
||||
if file_path.begins_with(PATH_TRASH):
|
||||
func is_trash_path(file_path:String) -> bool:
|
||||
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
|
||||
push_error(err_msg)
|
||||
console.err(err_msg)
|
||||
@ -710,14 +757,25 @@ func recycle(file_path:String):
|
||||
|
||||
# is dir?
|
||||
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)
|
||||
|
||||
if not d.dir_exists(PATH_TRASH):
|
||||
var _err = d.make_dir(PATH_TRASH)
|
||||
if not d.dir_exists(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(".new_path"), new_path)
|
||||
|
||||
@ -730,6 +788,10 @@ func recycle(file_path:String):
|
||||
if opened:
|
||||
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):
|
||||
if old_path == new_path or not old_path or not new_path:
|
||||
return
|
||||
@ -740,12 +802,12 @@ func rename_file(old_path:String, new_path:String):
|
||||
console.err(err_msg)
|
||||
return
|
||||
|
||||
var selected = get_selected_file()
|
||||
var was_selected = old_path == get_selected_file()
|
||||
if Directory.new().rename(old_path, new_path) == OK:
|
||||
refresh_files()
|
||||
if selected == old_path:
|
||||
_selected_file_changed(new_path)
|
||||
emit_signal("file_renamed", old_path, new_path)
|
||||
if was_selected:
|
||||
_selected_file_changed(new_path)
|
||||
|
||||
else:
|
||||
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):
|
||||
return
|
||||
|
||||
var temp = get_temporary_tab()
|
||||
if temp:
|
||||
if temp.file_path == file_path:
|
||||
temp.temporary = false
|
||||
else:
|
||||
temp.close()
|
||||
if is_opened(file_path):
|
||||
var tab = get_tab(file_path)
|
||||
if tab.temporary:
|
||||
tab.temporary = false
|
||||
|
||||
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)
|
||||
|
||||
# select current tab
|
||||
@ -829,8 +894,12 @@ func show_dir(fname:String, base_dir:String) -> bool:
|
||||
return true
|
||||
|
||||
func show_file(fname:String) -> bool:
|
||||
# hidden
|
||||
if fname.begins_with("."):
|
||||
if not show.file.hidden: return false
|
||||
# extensionless
|
||||
if not "." in fname:
|
||||
return show.file.extensionless
|
||||
|
||||
var ext = get_extension(fname)
|
||||
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 ""
|
||||
|
||||
var complained_ext:Array = []
|
||||
|
||||
func get_extension_helper(file_path:String) -> TE_ExtensionHelper:
|
||||
var ext:String = get_extension(file_path).replace(".", "_")
|
||||
var ext_path:String = "res://addons/text_editor/ext/ext_%s.gd" % ext
|
||||
if ext in ["cfg", "csv", "ini", "json", "md", "yaml"]:
|
||||
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()
|
||||
|
@ -50,10 +50,18 @@ func _ready():
|
||||
popup.add_font_override("font", editor.FONT)
|
||||
|
||||
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("Capitalize")
|
||||
popup.add_item("Variable")
|
||||
|
||||
# popup.add_shortcut()
|
||||
_e = popup.connect("index_pressed", self, "_popup_menu")
|
||||
|
||||
# hint
|
||||
@ -69,17 +77,49 @@ func _popup_menu(index:int):
|
||||
"Capitalize": selection_capitalize()
|
||||
"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():
|
||||
_remember_selection()
|
||||
insert_text_at_cursor(get_selection_text().to_upper())
|
||||
_remake_selection()
|
||||
|
||||
func selection_lowercase():
|
||||
_remember_selection()
|
||||
insert_text_at_cursor(get_selection_text().to_lower())
|
||||
_remake_selection()
|
||||
|
||||
func selection_variable():
|
||||
_remember_selection()
|
||||
insert_text_at_cursor(get_selection_text().to_lower().replace(" ", "_"))
|
||||
_remake_selection()
|
||||
|
||||
func selection_capitalize():
|
||||
_remember_selection()
|
||||
insert_text_at_cursor(get_selection_text().capitalize())
|
||||
_remake_selection()
|
||||
|
||||
func _node(n):
|
||||
var _e
|
||||
@ -131,6 +171,25 @@ func _file_renamed(old_path:String, new_path:String):
|
||||
update_name()
|
||||
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):
|
||||
if not editor.is_plugin_active():
|
||||
return
|
||||
@ -138,20 +197,14 @@ func _input(e):
|
||||
if not visible or not in_focus or not mouse_inside:
|
||||
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:
|
||||
var line:String = get_line(cursor_get_line())
|
||||
|
||||
# if selecting a symbol, show it in symbol viewer
|
||||
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
|
||||
|
||||
# click link
|
||||
var ca = line.find("(")
|
||||
var cb = line.find_last(")")
|
||||
if ca != -1 and cb != -1:
|
||||
@ -206,6 +259,11 @@ func _input(e):
|
||||
select(f+1, 0, t+1, len(get_line(t+1)))
|
||||
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):
|
||||
if not visible:
|
||||
return
|
||||
@ -249,11 +307,15 @@ func _file_selected(p:String):
|
||||
if had_selection:
|
||||
select(fl, fc, tl, tc)
|
||||
|
||||
grab_focus()
|
||||
grab_click_focus()
|
||||
|
||||
|
||||
func goto_line(line:int):
|
||||
# force scroll to bottom so selected line will be at top
|
||||
cursor_set_line(get_line_count())
|
||||
cursor_set_line(line)
|
||||
_update_selected_line()
|
||||
|
||||
func text_changed():
|
||||
if last_selected:
|
||||
|
@ -56,8 +56,12 @@ func _dir_popup(index:int):
|
||||
file = file.file_path
|
||||
|
||||
match dir_popup.get_item_text(index):
|
||||
"New File": editor.popup_create_file(file)
|
||||
"Remove": editor.recycle(file)
|
||||
"New File":
|
||||
editor.popup_create_file(file)
|
||||
|
||||
"Remove":
|
||||
editor.recycle(file, type == "f")
|
||||
|
||||
"Tint Yellow":
|
||||
selected[1].tint = Color.gold
|
||||
_redraw()
|
||||
@ -89,7 +93,7 @@ func _file_popup(index:int):
|
||||
|
||||
"Remove":
|
||||
if type == "f":
|
||||
editor.recycle(file)
|
||||
editor.recycle(file, true)
|
||||
|
||||
_:
|
||||
selected = []
|
||||
@ -119,9 +123,11 @@ func _input(e:InputEvent):
|
||||
if type in ["f", "d"]:
|
||||
var file_path = file if type == "f" else file.file_path
|
||||
|
||||
if file_path.begins_with(editor.PATH_TRASH):
|
||||
return # can't move recycling
|
||||
# can't move recycling
|
||||
if editor.is_trash_path(file_path):
|
||||
return
|
||||
|
||||
# select for drag
|
||||
else:
|
||||
dragging = meta_hovered
|
||||
|
||||
@ -134,9 +140,10 @@ func _input(e:InputEvent):
|
||||
var drag_type = dragging[0]
|
||||
var drag_file = dragging[1]
|
||||
|
||||
# dragged onto directory?
|
||||
if type == "d":
|
||||
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())
|
||||
editor.rename_file(old_path, new_path)
|
||||
|
||||
@ -151,7 +158,7 @@ func _input(e:InputEvent):
|
||||
|
||||
# unrecycle
|
||||
"unrecycle":
|
||||
editor.unrecycle(file)
|
||||
editor.unrecycle(file.file_path)
|
||||
|
||||
# select
|
||||
"f":
|
||||
@ -194,8 +201,8 @@ func _draw_dir(dir:Dictionary, deep:int):
|
||||
var head:String = "▼" if dir.open else "▶"
|
||||
head = clr(space+FOLDER+head, Color.white.darkened(.5))
|
||||
head += " " + b(file.get_file())
|
||||
var link:String = meta(head, ["d", dir], file)
|
||||
if file.begins_with(editor.PATH_TRASH) and file.count("/") == 3:
|
||||
var link:String = meta(head, ["d", dir], editor.get_localized_path(file))
|
||||
if editor.is_trash_path(file):
|
||||
link += " " + meta(clr("⬅", Color.yellowgreen), ["unrecycle", dir], file)
|
||||
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):
|
||||
var file_path = dir.files[i]
|
||||
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]
|
||||
var ext = p[1]
|
||||
|
||||
@ -226,11 +233,11 @@ func _draw_dir(dir:Dictionary, deep:int):
|
||||
if "readme" in file.to_lower():
|
||||
head = "🛈"
|
||||
|
||||
if is_selected:
|
||||
head = "● "
|
||||
|
||||
elif is_opened:
|
||||
head = "○ "
|
||||
# if is_selected or is_opened:
|
||||
# head = "● "
|
||||
#
|
||||
# elif is_opened:
|
||||
# head = "○ "
|
||||
|
||||
head = clr(head, Color.white.darkened(.5 if is_opened else .75))
|
||||
|
||||
@ -244,6 +251,14 @@ func _draw_dir(dir:Dictionary, deep:int):
|
||||
pass
|
||||
|
||||
file = clr(file, color)
|
||||
ext = clr("." + ext, Color.white.darkened(.65))
|
||||
var line = space + head + file + ext
|
||||
lines.append(meta(line, ["f", file_path], file_path))
|
||||
ext = "" if not ext else clr("." + ext, Color.white.darkened(.65))
|
||||
|
||||
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()
|
||||
|
||||
func _redraw():
|
||||
if not visible:
|
||||
return
|
||||
|
||||
var tab = editor.get_selected_tab()
|
||||
if tab:
|
||||
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 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 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 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():
|
||||
var btn = get_parent().get_node("update")
|
||||
@ -14,6 +16,10 @@ func _ready():
|
||||
func _update():
|
||||
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:
|
||||
var file = path.get_file()
|
||||
var ext = file.get_extension()
|
||||
@ -29,33 +35,62 @@ func _update():
|
||||
_sort()
|
||||
_redraw()
|
||||
|
||||
func _chapter(path:String, line:int, id:String):
|
||||
if not id:
|
||||
id = "???"
|
||||
chapter_info.append({ path=path, line=line, id=id, words=0, chaps=0, "%":0.0 })
|
||||
const WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
||||
const MONTHS = ["Januaray", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
|
||||
|
||||
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):
|
||||
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
|
||||
while i < len(lines):
|
||||
# skip head meta
|
||||
if i == 0 and lines[i].begins_with("---"):
|
||||
is_entire_file = true
|
||||
i += 1
|
||||
while i < len(lines) and not lines[i].begins_with("---"):
|
||||
if lines[i].begins_with("name: "):
|
||||
chapter_info[-1].id = lines[i].split("name: ", true, 1)[1]
|
||||
if ":" in lines[i]:
|
||||
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: "):
|
||||
chapter_info[-1]["%"] = float(lines[i].split("progress: ", true, 1)[1].replace("%", ""))
|
||||
elif lines[i].begins_with("prog: "):
|
||||
chapter_info[-1]["%"] = float(lines[i].split("prog: ", true, 1)[1].replace("%", ""))
|
||||
"prog", "progress":
|
||||
out["%"] = float(v.replace("%", ""))
|
||||
|
||||
i += 1
|
||||
|
||||
# skip comments
|
||||
elif "<!--" in lines[i]:
|
||||
pass
|
||||
|
||||
# skip code blocks
|
||||
elif lines[i].begins_with("~~~") or lines[i].begins_with("```"):
|
||||
var head = lines[i].substr(0, 3)
|
||||
@ -69,23 +104,37 @@ func _process_md(path:String):
|
||||
var id = lines[i].split(" ", true, 1)
|
||||
var deep = len(id[0])
|
||||
id = "???" if len(id) == 1 else id[1].strip_edges()
|
||||
if deep == 1 and not is_entire_file:
|
||||
_chapter(path, i, id)
|
||||
else:
|
||||
chapter_info[-1].chaps += 1
|
||||
out.chaps += 1
|
||||
|
||||
else:
|
||||
var words = lines[i].split(" ", false)
|
||||
chapter_info[-1].words += len(words)
|
||||
out.words += TE_Util.count_words(lines[i], out.uwords, skip_words)
|
||||
|
||||
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):
|
||||
match args[0]:
|
||||
"sort_table":
|
||||
var key = args[1]
|
||||
if sort_on != key:
|
||||
sort_on = key
|
||||
sort_on_index = sort_reverse.keys().find(sort_on)
|
||||
else:
|
||||
sort_reverse[key] = not sort_reverse[key]
|
||||
|
||||
@ -114,9 +163,14 @@ func _redraw():
|
||||
|
||||
var c1 = Color.white.darkened(.4)
|
||||
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_table(len(cols))
|
||||
add_constant_override("table_hseparation", 8)
|
||||
|
||||
for id in cols:
|
||||
push_cell()
|
||||
push_bold()
|
||||
@ -137,30 +191,51 @@ func _redraw():
|
||||
for i in len(chapter_info):
|
||||
var item = chapter_info[i]
|
||||
var clr = c1 if i%2==0 else c2
|
||||
var clrh = ch1 if i%2==0 else ch2
|
||||
|
||||
# id
|
||||
push_cell()
|
||||
push_color(clr)
|
||||
push_meta(add_meta(["goto", item.path, item.line], item.path))
|
||||
push_color(clrh if sort_on_index == 0 else clr)
|
||||
push_meta(add_meta(["goto", item.path, item.line], item.path + "\n" + item.uwords_all))
|
||||
add_text(item.id)
|
||||
pop()
|
||||
pop()
|
||||
pop()
|
||||
|
||||
# word cound
|
||||
for x in ["words", "chaps"]:
|
||||
# chapters
|
||||
push_cell()
|
||||
push_color(clr)
|
||||
add_text(TE_Util.commas(item[x]))
|
||||
push_color(clrh if sort_on_index == 1 else clr)
|
||||
add_text(TE_Util.commas(item.chaps))
|
||||
pop()
|
||||
pop()
|
||||
|
||||
# word cound
|
||||
push_cell()
|
||||
push_color(clrh if sort_on_index == 2 else clr)
|
||||
add_text(TE_Util.commas(item.words))
|
||||
pop()
|
||||
pop()
|
||||
|
||||
# unique words
|
||||
push_cell()
|
||||
push_color(clrh if sort_on_index == 3 else clr)
|
||||
add_text(item.uwords)
|
||||
pop()
|
||||
pop()
|
||||
|
||||
# percent
|
||||
push_cell()
|
||||
push_color(clr)
|
||||
push_color(clrh if sort_on_index == 4 else clr)
|
||||
add_text(str(int(item["%"])))
|
||||
pop()
|
||||
pop()
|
||||
|
||||
# time
|
||||
push_cell()
|
||||
push_color(clrh if sort_on_index == 5 else clr)
|
||||
add_text(item.time_nice)
|
||||
pop()
|
||||
pop()
|
||||
|
||||
pop()
|
||||
pop()
|
||||
|
@ -1,7 +1,9 @@
|
||||
tool
|
||||
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 last_search:String = ""
|
||||
@ -10,7 +12,10 @@ func _ready():
|
||||
var _e
|
||||
_e = line_edit.connect("text_entered", self, "_text_entered")
|
||||
|
||||
# fix fonts
|
||||
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):
|
||||
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:
|
||||
line_edit.grab_focus()
|
||||
line_edit.grab_click_focus()
|
||||
line_edit.select_all()
|
||||
# show meta tab
|
||||
get_parent().get_parent().show()
|
||||
# make search tab current meta tab
|
||||
get_parent().get_parent().current_tab = get_parent().get_index()
|
||||
get_tree().set_input_as_handled()
|
||||
|
||||
func _clicked(args):
|
||||
match args[0]:
|
||||
"goto":
|
||||
var tab = editor.open_file(args[1])
|
||||
var tab:TextEdit = editor.open_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):
|
||||
last_search = search_for
|
||||
@ -44,7 +59,7 @@ func _text_entered(search_for:String):
|
||||
|
||||
var file_path:String = fpaths[k]
|
||||
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)
|
||||
|
||||
for j in len(found[file_path]):
|
||||
@ -62,43 +77,76 @@ func _text_entered(search_for:String):
|
||||
if i == 2:
|
||||
var line:String = got_lines[i]
|
||||
var head:String = line.substr(0, char_index)
|
||||
var midd:String = line.substr(char_index, len(search_for)+1)
|
||||
var tail:String = line.substr(char_index+len(search_for)+1)
|
||||
head = clr(head, Color.tomato.lightened(.5))
|
||||
midd = clr(midd, Color.tomato.darkened(.25))
|
||||
tail = clr(tail, Color.tomato.lightened(.5))
|
||||
l = "\t" + clr(str(line_index-2+i+1) + ": ", Color.tomato.lightened(.5)) + (head+midd+tail)
|
||||
var midd:String = line.substr(char_index, len(search_for))
|
||||
var tail:String = line.substr(char_index+len(search_for))
|
||||
head = clr(head, Color.deepskyblue.lightened(.5))
|
||||
midd = clr(midd, Color.deepskyblue.darkened(.25))
|
||||
tail = clr(tail, Color.deepskyblue.lightened(.5))
|
||||
l = "\t" + clr(str(line_index-2+i+1) + ": ", Color.deepskyblue.lightened(.5)) + (head+midd+tail)
|
||||
|
||||
elif line_index-2+i >= 0:
|
||||
l = "\t" + clr(str(line_index-2+i+1) + ": ", Color.gray) + got_lines[i]
|
||||
|
||||
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)
|
||||
|
||||
set_bbcode(bbcode.join("\n"))
|
||||
|
||||
# get a list of files containging lines
|
||||
func _search(search_for:String) -> Dictionary:
|
||||
var found = {}
|
||||
var search_for_l = search_for.to_lower() # lowercase. TODO: match case
|
||||
for path in editor.file_paths:
|
||||
var out = {}
|
||||
var search_for_l:String
|
||||
|
||||
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")
|
||||
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
|
||||
var char_index:int = line.find(search_for_l)
|
||||
if char_index != -1:
|
||||
if not path in found:
|
||||
found[path] = []
|
||||
if not path in out:
|
||||
out[path] = []
|
||||
|
||||
var preview_lines = PoolStringArray()
|
||||
var highlight_from:int = line_index
|
||||
|
||||
# show surrounding 5 lines.
|
||||
for i in range(-2, 3):
|
||||
if line_index+i >= 0 and line_index+i < len(lines):
|
||||
preview_lines.append(lines[line_index+i])
|
||||
else:
|
||||
preview_lines.append("")
|
||||
|
||||
# lines, index in file, index in line
|
||||
found[path].append([preview_lines, line_index, char_index])
|
||||
return found
|
||||
out[path].append([preview_lines, line_index, char_index])
|
||||
|
||||
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"
|
||||
|
||||
var hscrolls:Dictionary = {}
|
||||
var selected_line:int = 0
|
||||
var current_file:String = ""
|
||||
|
||||
func _ready():
|
||||
var _e
|
||||
_e = editor.connect("symbols_updated", self, "_redraw")
|
||||
_e = editor.connect("tags_updated", self, "_redraw")
|
||||
_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 = get_v_scroll().connect("value_changed", self, "_scrolling")
|
||||
|
||||
@ -19,13 +23,25 @@ func _ready():
|
||||
call_deferred("_redraw")
|
||||
|
||||
func _selected_symbol_line(line:int):
|
||||
# scroll_to_line(get_line_count()-1)
|
||||
scroll_to_line(line)
|
||||
selected_line = clamp(line, 0, get_line_count())
|
||||
scroll_to_line(clamp(line-1, 0, get_line_count()-1))
|
||||
_redraw()
|
||||
|
||||
func _file_selected(file_path:String):
|
||||
current_file = file_path
|
||||
yield(get_tree(), "idle_frame")
|
||||
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):
|
||||
hscrolls[editor.get_selected_file()] = get_v_scroll().value
|
||||
|
||||
@ -79,8 +95,9 @@ func _redraw():
|
||||
|
||||
else:
|
||||
var t = PoolStringArray()
|
||||
|
||||
var i = -1
|
||||
for line_index in symbols:
|
||||
i += 1
|
||||
if line_index == -1:
|
||||
continue # special file chapter
|
||||
var symbol_info = symbols[line_index]
|
||||
@ -102,6 +119,10 @@ func _redraw():
|
||||
cl = cl.darkened(.33 * (deep-1))
|
||||
|
||||
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"))
|
||||
|
@ -1,5 +1,74 @@
|
||||
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:
|
||||
var f:File = File.new()
|
||||
if f.file_exists(path):
|
||||
|
@ -206,7 +206,7 @@ split_offset = -200
|
||||
|
||||
[node name="tab_container" type="TabContainer" parent="c/div1/div2/c/c"]
|
||||
margin_right = 614.0
|
||||
margin_bottom = 562.0
|
||||
margin_bottom = 283.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
custom_fonts/font = ExtResource( 12 )
|
||||
@ -218,13 +218,13 @@ __meta__ = {
|
||||
}
|
||||
|
||||
[node name="meta_tabs" type="TabContainer" parent="c/div1/div2/c/c"]
|
||||
visible = false
|
||||
margin_top = 288.0
|
||||
margin_top = 295.0
|
||||
margin_right = 614.0
|
||||
margin_bottom = 566.0
|
||||
margin_bottom = 562.0
|
||||
script = ExtResource( 16 )
|
||||
|
||||
[node name="console" type="RichTextLabel" parent="c/div1/div2/c/c/meta_tabs"]
|
||||
visible = false
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 4.0
|
||||
@ -265,7 +265,6 @@ bbcode_enabled = true
|
||||
script = ExtResource( 9 )
|
||||
|
||||
[node name="search" type="VBoxContainer" parent="c/div1/div2/c/c/meta_tabs"]
|
||||
visible = false
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 4.0
|
||||
@ -275,16 +274,33 @@ margin_bottom = -4.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="le" type="LineEdit" parent="c/div1/div2/c/c/meta_tabs/search"]
|
||||
margin_right = 58.0
|
||||
margin_bottom = 24.0
|
||||
[node name="c" type="HBoxContainer" parent="c/div1/div2/c/c/meta_tabs/search"]
|
||||
margin_right = 606.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
|
||||
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"]
|
||||
margin_top = 28.0
|
||||
margin_right = 58.0
|
||||
margin_bottom = 28.0
|
||||
margin_top = 31.0
|
||||
margin_right = 606.0
|
||||
margin_bottom = 231.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme = SubResource( 4 )
|
||||
@ -306,17 +322,16 @@ margin_right = -4.0
|
||||
margin_bottom = -4.0
|
||||
|
||||
[node name="update" type="Button" parent="c/div1/div2/c/c/meta_tabs/sys"]
|
||||
margin_right = 12.0
|
||||
margin_bottom = 20.0
|
||||
margin_right = 606.0
|
||||
margin_bottom = 28.0
|
||||
size_flags_horizontal = 3
|
||||
custom_fonts/font = ExtResource( 12 )
|
||||
text = "⟳"
|
||||
|
||||
[node name="sys" type="RichTextLabel" parent="c/div1/div2/c/c/meta_tabs/sys"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_right = -8.0
|
||||
margin_bottom = -36.0
|
||||
margin_top = 32.0
|
||||
margin_right = 606.0
|
||||
margin_bottom = 232.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
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 lines = TE_Util.commas(len(TE_Util.split_many(t.text, ".?!\n", false)))
|
||||
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([
|
||||
["chars", "words", "lines", "bytes"],
|
||||
[chars, words, lines, bytes]
|
||||
|
@ -1,12 +1,132 @@
|
||||
tool
|
||||
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=" -->"):
|
||||
return .toggle_comment(t, head, tail)
|
||||
|
||||
func apply_colors(e:TE_Editor, t:TextEdit):
|
||||
.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("false", e.color_var)
|
||||
|
||||
@ -59,7 +179,6 @@ func apply_colors(e:TE_Editor, t:TextEdit):
|
||||
# tables
|
||||
t.add_color_region("|", "", Color.tan, true)
|
||||
|
||||
|
||||
func get_symbols(t:String) -> Dictionary:
|
||||
var out = .get_symbols(t)
|
||||
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]
|
||||
|
||||
[resource]
|
||||
size = 12
|
||||
use_filter = true
|
||||
font_data = ExtResource( 1 )
|
||||
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]
|
||||
|
||||
[resource]
|
||||
size = 12
|
||||
use_filter = true
|
||||
font_data = ExtResource( 1 )
|
||||
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]
|
||||
|
||||
[resource]
|
||||
size = 12
|
||||
use_filter = true
|
||||
font_data = ExtResource( 1 )
|
||||
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]
|
||||
|
||||
[resource]
|
||||
size = 12
|
||||
use_filter = true
|
||||
font_data = ExtResource( 1 )
|
||||
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]
|
||||
|
||||
[resource]
|
||||
size = 12
|
||||
use_filter = true
|
||||
font_data = ExtResource( 1 )
|
||||
fallback/0 = ExtResource( 3 )
|
||||
|
@ -3,5 +3,5 @@
|
||||
name="TextEditor"
|
||||
description="A text editor for Godot."
|
||||
author="teebar"
|
||||
version="1.6"
|
||||
version="1.7"
|
||||
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