mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-11-12 21:50:26 +01:00
539 lines
13 KiB
GDScript
539 lines
13 KiB
GDScript
extends Control
|
|
@tool
|
|
|
|
var _interface = null
|
|
var _utils = load("res://addons/gut/utils.gd").new()
|
|
var _hide_passing = true
|
|
var _font = null
|
|
var _font_size = null
|
|
var _root = null
|
|
var _max_icon_width = 10
|
|
var _editors = null # script_text_editor_controls.gd
|
|
var _show_orphans = true
|
|
var _output_control = null
|
|
|
|
const _col_1_bg_color = Color(0, 0, 0, .1)
|
|
|
|
var _icons = {
|
|
red = load("res://addons/gut/images/red.png"),
|
|
green = load("res://addons/gut/images/green.png"),
|
|
yellow = load("res://addons/gut/images/yellow.png"),
|
|
}
|
|
|
|
signal search_for_text(text)
|
|
|
|
@onready var _ctrls = {
|
|
tree = $VBox/Output/Scroll/Tree,
|
|
lbl_overlay = $VBox/Output/OverlayMessage,
|
|
chk_hide_passing = $VBox/Toolbar/HidePassing,
|
|
toolbar =
|
|
{
|
|
toolbar = $VBox/Toolbar,
|
|
collapse = $VBox/Toolbar/Collapse,
|
|
collapse_all = $VBox/Toolbar/CollapseAll,
|
|
expand = $VBox/Toolbar/Expand,
|
|
expand_all = $VBox/Toolbar/ExpandAll,
|
|
hide_passing = $VBox/Toolbar/HidePassing,
|
|
show_script = $VBox/Toolbar/ShowScript,
|
|
scroll_output = $VBox/Toolbar/ScrollOutput
|
|
}
|
|
}
|
|
|
|
|
|
func _test_running_setup():
|
|
_hide_passing = true
|
|
_show_orphans = true
|
|
var _gut_config = load("res://addons/gut/gut_config.gd").new()
|
|
_gut_config.load_panel_options("res://.gut_editor_config.json")
|
|
set_font(
|
|
_gut_config.options.panel_options.font_name, _gut_config.options.panel_options.font_size
|
|
)
|
|
|
|
_ctrls.toolbar.hide_passing.text = "[hp]"
|
|
load_json_file("user://.gut_editor.json")
|
|
|
|
|
|
func _set_toolbutton_icon(btn, icon_name, text):
|
|
if Engine.is_editor_hint():
|
|
btn.icon = get_theme_icon(icon_name, "EditorIcons")
|
|
else:
|
|
btn.text = str("[", text, "]")
|
|
|
|
|
|
func _ready():
|
|
var f = null
|
|
if $FontSampler.get_label_settings() == null:
|
|
f = get_theme_default_font()
|
|
else:
|
|
f = $FontSampler.get_label_settings().font
|
|
var s_size = f.get_string_size("000 of 000 passed")
|
|
_root = _ctrls.tree.create_item()
|
|
_ctrls.tree.set_hide_root(true)
|
|
_ctrls.tree.columns = 2
|
|
_ctrls.tree.set_column_expand(0, true)
|
|
_ctrls.tree.set_column_expand(1, false)
|
|
_ctrls.tree.set_column_custom_minimum_width(1, s_size.x)
|
|
|
|
_set_toolbutton_icon(_ctrls.toolbar.collapse, "CollapseTree", "c")
|
|
_set_toolbutton_icon(_ctrls.toolbar.collapse_all, "CollapseTree", "c")
|
|
_set_toolbutton_icon(_ctrls.toolbar.expand, "ExpandTree", "e")
|
|
_set_toolbutton_icon(_ctrls.toolbar.expand_all, "ExpandTree", "e")
|
|
_set_toolbutton_icon(_ctrls.toolbar.show_script, "Script", "ss")
|
|
_set_toolbutton_icon(_ctrls.toolbar.scroll_output, "Font", "so")
|
|
|
|
(
|
|
_ctrls
|
|
. toolbar
|
|
. hide_passing
|
|
. set("custom_icons/checked", get_theme_icon("GuiVisibilityHidden", "EditorIcons"))
|
|
)
|
|
(
|
|
_ctrls
|
|
. toolbar
|
|
. hide_passing
|
|
. set("custom_icons/unchecked", get_theme_icon("GuiVisibilityVisible", "EditorIcons"))
|
|
)
|
|
|
|
if get_parent() == get_tree().root:
|
|
_test_running_setup()
|
|
|
|
call_deferred("_update_min_width")
|
|
|
|
|
|
func _update_min_width():
|
|
custom_minimum_size.x = _ctrls.toolbar.toolbar.size.x
|
|
|
|
|
|
func _open_file(path, line_number):
|
|
if _interface == null:
|
|
print("Too soon, wait a bit and try again.")
|
|
return
|
|
|
|
var r = load(path)
|
|
if line_number != -1:
|
|
_interface.edit_script(r, line_number)
|
|
else:
|
|
_interface.edit_script(r)
|
|
|
|
if _ctrls.toolbar.show_script.pressed:
|
|
_interface.set_main_screen_editor("Script")
|
|
|
|
|
|
func _add_script_tree_item(script_path, script_json):
|
|
var path_info = _get_path_and_inner_class_name_from_test_path(script_path)
|
|
# print('* adding script ', path_info)
|
|
var item_text = script_path
|
|
var parent = _root
|
|
|
|
if path_info.inner_class != "":
|
|
parent = _find_script_item_with_path(path_info.path)
|
|
item_text = path_info.inner_class
|
|
if parent == null:
|
|
parent = _add_script_tree_item(path_info.path, {})
|
|
|
|
var item = _ctrls.tree.create_item(parent)
|
|
item.set_text(0, item_text)
|
|
var meta = {
|
|
"type": "script",
|
|
"path": path_info.path,
|
|
"inner_class": path_info.inner_class,
|
|
"json": script_json
|
|
}
|
|
item.set_metadata(0, meta)
|
|
item.set_custom_bg_color(1, _col_1_bg_color)
|
|
|
|
return item
|
|
|
|
|
|
func _add_assert_item(text, icon, parent_item):
|
|
# print(' * adding assert')
|
|
var assert_item = _ctrls.tree.create_item(parent_item)
|
|
assert_item.set_icon_max_width(0, _max_icon_width)
|
|
assert_item.set_text(0, text)
|
|
assert_item.set_metadata(0, {"type": "assert"})
|
|
assert_item.set_icon(0, icon)
|
|
assert_item.set_custom_bg_color(1, _col_1_bg_color)
|
|
|
|
return assert_item
|
|
|
|
|
|
func _add_test_tree_item(test_name, test_json, script_item):
|
|
# print(' * adding test ', test_name)
|
|
var no_orphans_to_show = !_show_orphans or (_show_orphans and test_json.orphans == 0)
|
|
if _hide_passing and test_json["status"] == "pass" and no_orphans_to_show:
|
|
return
|
|
|
|
var item = _ctrls.tree.create_item(script_item)
|
|
var status = test_json["status"]
|
|
var meta = {"type": "test", "json": test_json}
|
|
|
|
item.set_text(0, test_name)
|
|
item.set_text(1, status)
|
|
item.set_text_alignment(1, HORIZONTAL_ALIGNMENT_RIGHT)
|
|
item.set_custom_bg_color(1, _col_1_bg_color)
|
|
|
|
item.set_metadata(0, meta)
|
|
item.set_icon_max_width(0, _max_icon_width)
|
|
|
|
var orphan_text = "orphans"
|
|
if test_json.orphans == 1:
|
|
orphan_text = "orphan"
|
|
orphan_text = str(test_json.orphans, " ", orphan_text)
|
|
|
|
if status == "pass" and no_orphans_to_show:
|
|
item.set_icon(0, _icons.green)
|
|
elif status == "pass" and !no_orphans_to_show:
|
|
item.set_icon(0, _icons.yellow)
|
|
item.set_text(1, orphan_text)
|
|
elif status == "fail":
|
|
item.set_icon(0, _icons.red)
|
|
else:
|
|
item.set_icon(0, _icons.yellow)
|
|
|
|
if !_hide_passing:
|
|
for passing in test_json.passing:
|
|
_add_assert_item("pass: " + passing, _icons.green, item)
|
|
|
|
for failure in test_json.failing:
|
|
_add_assert_item("fail: " + failure.replace("\n", ""), _icons.red, item)
|
|
|
|
for pending in test_json.pending:
|
|
_add_assert_item("pending: " + pending.replace("\n", ""), _icons.yellow, item)
|
|
|
|
if status != "pass" and !no_orphans_to_show:
|
|
_add_assert_item(orphan_text, _icons.yellow, item)
|
|
|
|
return item
|
|
|
|
|
|
func _load_result_tree(j):
|
|
var scripts = j["test_scripts"]["scripts"]
|
|
var script_keys = scripts.keys()
|
|
# if we made it here, the json is valid and we did something, otherwise the
|
|
# 'nothing to see here' should be visible.
|
|
clear_centered_text()
|
|
|
|
var _last_script_item = null
|
|
for key in script_keys:
|
|
var tests = scripts[key]["tests"]
|
|
var test_keys = tests.keys()
|
|
var s_item = _add_script_tree_item(key, scripts[key])
|
|
var bad_count = 0
|
|
|
|
for test_key in test_keys:
|
|
var t_item = _add_test_tree_item(test_key, tests[test_key], s_item)
|
|
if tests[test_key].status != "pass":
|
|
bad_count += 1
|
|
elif t_item != null:
|
|
t_item.collapsed = true
|
|
|
|
# get_children returns the first child or null. its a dumb name.
|
|
if s_item.get_children() == null:
|
|
# var m = s_item.get_metadata(0)
|
|
# print('!! Deleting ', m.path, ' ', m.inner_class)
|
|
s_item.free()
|
|
else:
|
|
var total_text = str(test_keys.size(), " passed")
|
|
# s_item.set_text_alignment(1, s_item.ALIGN_LEFT)
|
|
if bad_count == 0:
|
|
s_item.collapsed = true
|
|
else:
|
|
total_text = str(test_keys.size() - bad_count, " of ", test_keys.size(), " passed")
|
|
s_item.set_text(1, total_text)
|
|
|
|
_free_childless_scripts()
|
|
_show_all_passed()
|
|
|
|
|
|
func _free_childless_scripts():
|
|
var items = _root.get_children()
|
|
for item in items:
|
|
var next_item = item.get_next()
|
|
if item.get_children() == null:
|
|
item.free()
|
|
item = next_item
|
|
|
|
|
|
func _find_script_item_with_path(path):
|
|
var items = _root.get_children()
|
|
var to_return = null
|
|
|
|
var idx = 0
|
|
while idx < items.size() and to_return == null:
|
|
var item = items[idx]
|
|
if item.get_metadata(0).path == path:
|
|
to_return = item
|
|
else:
|
|
idx += 1
|
|
|
|
return to_return
|
|
|
|
|
|
func _get_line_number_from_assert_msg(msg):
|
|
var line = -1
|
|
if msg.find("at line") > 0:
|
|
line = msg.split("at line")[-1].split(" ")[-1].to_int()
|
|
return line
|
|
|
|
|
|
func _get_path_and_inner_class_name_from_test_path(path):
|
|
var to_return = {path = "", inner_class = ""}
|
|
|
|
to_return.path = path
|
|
if !path.ends_with(".gd"):
|
|
var loc = path.find(".gd")
|
|
to_return.inner_class = path.split(".")[-1]
|
|
to_return.path = path.substr(0, loc + 3)
|
|
return to_return
|
|
|
|
|
|
func _handle_tree_item_select(item, force_scroll):
|
|
var item_type = item.get_metadata(0).type
|
|
|
|
var path = ""
|
|
var line = -1
|
|
var method_name = ""
|
|
var inner_class = ""
|
|
|
|
if item_type == "test":
|
|
var s_item = item.get_parent()
|
|
path = s_item.get_metadata(0)["path"]
|
|
inner_class = s_item.get_metadata(0)["inner_class"]
|
|
line = -1
|
|
method_name = item.get_text(0)
|
|
elif item_type == "assert":
|
|
var s_item = item.get_parent().get_parent()
|
|
path = s_item.get_metadata(0)["path"]
|
|
inner_class = s_item.get_metadata(0)["inner_class"]
|
|
|
|
line = _get_line_number_from_assert_msg(item.get_text(0))
|
|
method_name = item.get_parent().get_text(0)
|
|
elif item_type == "script":
|
|
path = item.get_metadata(0)["path"]
|
|
if item.get_parent() != _root:
|
|
inner_class = item.get_text(0)
|
|
line = -1
|
|
method_name = ""
|
|
else:
|
|
return
|
|
|
|
var path_info = _get_path_and_inner_class_name_from_test_path(path)
|
|
if force_scroll or _ctrls.toolbar.show_script.pressed:
|
|
_goto_code(path, line, method_name, inner_class)
|
|
if force_scroll or _ctrls.toolbar.scroll_output.pressed:
|
|
_goto_output(path, method_name, inner_class)
|
|
|
|
|
|
# starts at beginning of text edit and searches for each search term, moving
|
|
# through the text as it goes; ensuring that, when done, it found the first
|
|
# occurance of the last srting that happend after the first occurance of
|
|
# each string before it. (Generic way of searching for a method name in an
|
|
# inner class that may have be a duplicate of a method name in a different
|
|
# inner class)
|
|
func _get_line_number_for_seq_search(search_strings, te):
|
|
# var te = _editors.get_current_text_edit()
|
|
var result = null
|
|
var line = Vector2i(-1, -1)
|
|
var s_flags = 0
|
|
|
|
var i = 0
|
|
var string_found = true
|
|
while i < search_strings.size() and string_found:
|
|
result = te.search(search_strings[i], s_flags, line.y, line.x)
|
|
if result.x != -1:
|
|
line = result
|
|
else:
|
|
string_found = false
|
|
i += 1
|
|
|
|
return line.y
|
|
|
|
|
|
func _goto_code(path, line, method_name = "", inner_class = ""):
|
|
if _interface == null:
|
|
print("going to ", [path, line, method_name, inner_class])
|
|
return
|
|
|
|
_open_file(path, line)
|
|
if line == -1:
|
|
var search_strings = []
|
|
if inner_class != "":
|
|
search_strings.append(inner_class)
|
|
|
|
if method_name != "":
|
|
search_strings.append(method_name)
|
|
|
|
line = _get_line_number_for_seq_search(search_strings, _editors.get_current_text_edit())
|
|
if line != -1:
|
|
_interface.get_script_editor().goto_line(line)
|
|
|
|
|
|
func _goto_output(path, method_name, inner_class):
|
|
if _output_control == null:
|
|
return
|
|
|
|
var search_strings = [path]
|
|
|
|
if inner_class != "":
|
|
search_strings.append(inner_class)
|
|
|
|
if method_name != "":
|
|
search_strings.append(method_name)
|
|
|
|
var line = _get_line_number_for_seq_search(search_strings, _output_control.get_rich_text_edit())
|
|
if line != -1:
|
|
_output_control.scroll_to_line(line)
|
|
|
|
|
|
func _show_all_passed():
|
|
if _root.get_children() == null:
|
|
add_centered_text("Everything passed!")
|
|
|
|
|
|
func _set_collapsed_on_all(item, value):
|
|
if item == _root:
|
|
var node = _root.get_children()
|
|
while node != null:
|
|
node.call_recursive("set_collapsed", value)
|
|
node = node.get_next()
|
|
else:
|
|
item.call_recursive("set_collapsed", value)
|
|
|
|
|
|
# --------------
|
|
# Events
|
|
# --------------
|
|
func _on_Tree_item_selected():
|
|
# do not force scroll
|
|
var item = _ctrls.tree.get_selected()
|
|
_handle_tree_item_select(item, false)
|
|
# it just looks better if the left is always selected.
|
|
if item.is_selected(1):
|
|
item.deselect(1)
|
|
item.select(0)
|
|
|
|
|
|
func _on_Tree_item_activated():
|
|
# force scroll
|
|
print("double clicked")
|
|
_handle_tree_item_select(_ctrls.tree.get_selected(), true)
|
|
|
|
|
|
func _on_Collapse_pressed():
|
|
collapse_selected()
|
|
|
|
|
|
func _on_Expand_pressed():
|
|
expand_selected()
|
|
|
|
|
|
func _on_CollapseAll_pressed():
|
|
collapse_all()
|
|
|
|
|
|
func _on_ExpandAll_pressed():
|
|
expand_all()
|
|
|
|
|
|
func _on_Hide_Passing_pressed():
|
|
_hide_passing = _ctrls.toolbar.hide_passing.button_pressed
|
|
|
|
|
|
# --------------
|
|
# Public
|
|
# --------------
|
|
func load_json_file(path):
|
|
var text = _utils.get_file_as_text(path)
|
|
if text != "":
|
|
var test_json_conv = JSON.new()
|
|
test_json_conv.parse(text)
|
|
var result = test_json_conv.get_data()
|
|
if result.error != OK:
|
|
add_centered_text(
|
|
str(
|
|
path,
|
|
" has invalid json in it \n",
|
|
"Error ",
|
|
result.error,
|
|
"@",
|
|
result.error_line,
|
|
"\n",
|
|
result.error_string
|
|
)
|
|
)
|
|
return
|
|
|
|
load_json_results(result.result)
|
|
else:
|
|
add_centered_text(str(path, " was empty or does not exist."))
|
|
|
|
|
|
func load_json_results(j):
|
|
clear()
|
|
add_centered_text("Nothing Here")
|
|
_load_result_tree(j)
|
|
|
|
|
|
func add_centered_text(t):
|
|
_ctrls.lbl_overlay.text = t
|
|
|
|
|
|
func clear_centered_text():
|
|
_ctrls.lbl_overlay.text = ""
|
|
|
|
|
|
func clear():
|
|
_ctrls.tree.clear()
|
|
_root = _ctrls.tree.create_item()
|
|
clear_centered_text()
|
|
|
|
|
|
func set_interface(which):
|
|
_interface = which
|
|
|
|
|
|
func set_script_text_editors(value):
|
|
_editors = value
|
|
|
|
|
|
func collapse_all():
|
|
_set_collapsed_on_all(_root, true)
|
|
|
|
|
|
func expand_all():
|
|
_set_collapsed_on_all(_root, false)
|
|
|
|
|
|
func collapse_selected():
|
|
var item = _ctrls.tree.get_selected()
|
|
if item != null:
|
|
_set_collapsed_on_all(item, true)
|
|
|
|
|
|
func expand_selected():
|
|
var item = _ctrls.tree.get_selected()
|
|
if item != null:
|
|
_set_collapsed_on_all(item, false)
|
|
|
|
|
|
func set_show_orphans(should):
|
|
_show_orphans = should
|
|
|
|
|
|
func set_font(font_name, size):
|
|
pass
|
|
|
|
|
|
# var dyn_font = FontFile.new()
|
|
# var font_data = FontFile.new()
|
|
# font_data.font_path = 'res://addons/gut/fonts/' + font_name + '-Regular.ttf'
|
|
# font_data.antialiased = true
|
|
# dyn_font.font_data = font_data
|
|
#
|
|
# _font = dyn_font
|
|
# _font.size = size
|
|
# _font_size = size
|
|
|
|
|
|
func set_output_control(value):
|
|
_output_control = value
|