myspace / CommonLua /GedInspector.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
9.09 kB
if FirstLoad then
GedInspectedObjects = setmetatable({}, weak_keys_meta)
GedInspectedObjectsRefCount = setmetatable({}, weak_keys_meta)
GedObjInspectors = {}
end
local function PrintTable(obj)
if obj and obj.class then
return "class " .. tostring(obj.class)
elseif IsT(obj) then
local tcontent = ""
local id = TGetID(obj)
if id then
tcontent = tostring(id)
end
if obj[1] then
tcontent = tcontent .. ", " .. tostring(obj[1])
end
if THasArgs(obj) then
tcontent = tcontent .. ", ..."
end
return "T{".. tcontent .. "}"
else
return tostring(obj)
end
end
local function PrintValue(obj)
local t = type(obj)
if t == "string" then
return "\"" .. obj .. "\""
elseif t == "table" then
return PrintTable(obj)
else
return tostring(obj)
end
end
local function UpdateObjRefCounts(old_obj, new_obj)
if old_obj and GedInspectedObjectsRefCount[old_obj] == 1 then
GedInspectedObjectsRefCount[old_obj] = nil
GedInspectedObjects[old_obj] = nil
elseif old_obj and GedInspectedObjectsRefCount[old_obj] then
GedInspectedObjectsRefCount[old_obj] = GedInspectedObjectsRefCount[old_obj] - 1
end
if new_obj and GedInspectedObjectsRefCount[new_obj] then
GedInspectedObjectsRefCount[new_obj] = GedInspectedObjectsRefCount[new_obj] + 1
elseif new_obj then
GedInspectedObjectsRefCount[new_obj] = 1
local table_list = {}
GedInspectedObjects[new_obj] = table_list
if type(new_obj) ~= "thread" then
-- gather table links
for key, value in next, new_obj do
if type(value) == "table" then
table.insert(table_list, value)
end
if type(key) == "table" then
table.insert(table_list, key)
end
end
end
local meta = getmetatable(new_obj)
if meta then
table.insert(table_list, meta)
end
end
end
local function get_thread_frame_ref(obj, level, info)
local ref_list = GedInspectedObjects[obj]
local data = {}
local l = 1
while true do
local name, val = debug.getlocal (obj, level, l)
if not name then
break
end
data[name] = val
l = l + 1
end
ref_list[level] = data
return level
end
function GedInspectorFormatObject(obj)
local members = {}
local ref_list = GedInspectedObjects[obj]
-- The object should have already been created by a call to UpdateObjRefCounts so we can have proper ref count
assert(ref_list)
if type(obj) == "thread" then
local info, level = true, 0
while true do
info = debug.getinfo(obj, level, "Slfun")
if not info then
break
end
local entry = {}
entry.key = level
entry.value = info.short_src .. "(" .. info.currentline .. ") "
.. (info.name or info.name_what or "unknown name")
entry.value_id = get_thread_frame_ref(obj, level, info)
table.insert(members, entry)
level = level + 1
end
else
for key, value in next, obj do
local entry = {}
entry.key = key
entry.value = value
if IsT(value) then
entry.value = _InternalTranslate(value)
elseif type(value) == "table" and not value.class then
local count = #value
if (count or 0) > 0 then
entry.count = count
end
end
local make_ref = function(value)
if type(value) == "table" or type(value) == "thread" then
local ref_id = table.find(ref_list, value)
if not ref_id then
table.insert(ref_list, value)
ref_id = #ref_list
end
return ref_id
end
end
entry.value_id = make_ref(value)
entry.key_id = make_ref(key)
table.insert(members, entry)
end
end
table.sort(members, function(a, b)
local a, b = a.key, b.key
if type(a) == "number" and type(b) == "number" then
return a < b
elseif type(a) == "number" then
return false
elseif type(b) == "number" then
return true
end
return tostring(a) < tostring(b)
end)
-- Convert the values to strings after they are sorted.
for k, v in ipairs(members) do
v.key = PrintValue(v.key)
v.value = PrintValue(v.value)
end
local context = {members=members, name=PrintValue(obj)}
local metatable = getmetatable(obj)
if metatable then
context.metatable_name = PrintValue(metatable)
context.metatable_id = table.find(ref_list, metatable)
end
return context
end
local function SetToggleActionStates(socket)
local obj = socket:ResolveObj("root")
socket:Send("rfnApp", "SetActionToggled", "SetO1", rawget(_G, "o1") == obj)
socket:Send("rfnApp", "SetActionToggled", "SetO2", rawget(_G, "o2") == obj)
socket:Send("rfnApp", "SetActionToggled", "SetO3", rawget(_G, "o3") == obj)
end
local function GedBindObjDirect(socket, new_obj)
UpdateObjRefCounts(socket:ResolveObj("root"), new_obj)
socket:BindObj("root", new_obj)
SetToggleActionStates(socket)
end
-- also writes to navigation history
function GedOpBindObjByRefId(socket, obj, ref_id, new_inspector)
local obj_ref_list = GedInspectedObjects[obj]
local new_obj = obj_ref_list[ref_id]
if new_obj then
if new_inspector then
Inspect(new_obj)
else
socket.nav_pos = socket.nav_pos + 1
while #socket.nav_list > socket.nav_pos do
table.remove(socket.nav_list, #socket.nav_list)
end
socket.nav_list[socket.nav_pos] = {obj = new_obj, parent_pos = socket.nav_pos - 1}
socket:SetSearchString(false)
GedBindObjDirect(socket, new_obj)
end
end
end
local function GedParentOf(socket, child_obj)
local nav_list = socket.nav_list
local pos = socket.nav_pos
local current = nav_list[pos]
local parent = nav_list[current.parent_pos]
return parent, current.parent_pos
end
local function GedParentOfIndexedObj(socket, child_obj)
local parent, pos = GedParentOf(socket, child_obj)
local idx = table.find(parent.obj, child_obj)
if idx then
return parent, pos, idx
end
end
function GedOpInspectorNavGo(socket, obj, subcmd)
local nav_list = socket.nav_list
local nav_pos = socket.nav_pos
if subcmd == "back" and socket.nav_pos > 1 then
socket.nav_pos = socket.nav_pos - 1
if nav_list[socket.nav_pos].parent_pos ~= nav_list[socket.nav_pos + 1].parent_pos then
socket:SetSearchString(false)
end
GedBindObjDirect(socket, nav_list[socket.nav_pos].obj)
elseif subcmd == "forward" and socket.nav_pos < #socket.nav_list then
socket.nav_pos = socket.nav_pos + 1
if nav_list[socket.nav_pos].parent_pos ~= nav_list[socket.nav_pos - 1].parent_pos then
socket:SetSearchString(false)
end
GedBindObjDirect(socket, nav_list[socket.nav_pos].obj)
elseif subcmd == "parent" then
local parent, nav_pos = GedParentOf(socket, obj)
if parent then
socket.nav_pos = nav_pos
socket:SetSearchString(false)
GedBindObjDirect(socket, parent.obj)
end
elseif subcmd == "nextchild" then
local parent, _, child_idx = GedParentOfIndexedObj(socket, obj)
if parent then
local candidate = parent.obj[child_idx + 1]
if candidate then
socket.nav_pos = socket.nav_pos + 1
socket.nav_list[socket.nav_pos] = { obj = candidate, parent_pos = nav_list[nav_pos].parent_pos }
GedBindObjDirect(socket, candidate)
end
end
elseif subcmd == "prevchild" then
local parent, _, child_idx = GedParentOfIndexedObj(socket, obj)
if parent then
local candidate = parent.obj[child_idx - 1]
if candidate then
socket.nav_pos = socket.nav_pos + 1
socket.nav_list[socket.nav_pos] = { obj = candidate, parent_pos = nav_list[nav_pos].parent_pos }
GedBindObjDirect(socket, candidate)
end
end
end
end
function GedOpInspectorSetGlobal(socket, obj, global_name, toggle)
assert(table.find({"o1", "o2", "o3"}, global_name))
if toggle then
rawset(_G, global_name, obj)
else
rawset(_G, global_name, nil)
end
for _, inspector in ipairs(GedObjInspectors) do
SetToggleActionStates(inspector)
end
end
function GedOpInspectorViewObject(socket, obj)
ViewAndSelectObject(obj)
end
function OnMsg.GedOpened(ged_id)
local gedApp = GedConnections[ged_id]
if gedApp and gedApp.app_template == "GedInspector" then
table.insert(GedObjInspectors, gedApp)
SetToggleActionStates(gedApp)
end
end
function OnMsg.GedClosing(ged_id)
local socket = table.find_value(GedObjInspectors, "ged_id", ged_id)
table.remove_value(GedObjInspectors, socket)
if socket then
local root = socket:ResolveObj("root")
UpdateObjRefCounts(root, nil)
end
end
function Inspect(object)
if type(object) ~= "table" then
print("Only tables can be inspected")
return
end
if not CanYield() then
CreateRealTimeThread(Inspect, object)
return
end
UpdateObjRefCounts(nil, object)
local app = OpenGedApp("GedInspector", object)
if app then
app.nav_list = {{obj = object, parent_pos = 1}}
app.nav_pos = 1
end
return app
end