Adds proc to show variable type as string, adds this info to Varedit (#10442)

Adds a proc that prints the variable type, optionally with some extra info like text/list length, types, etc. Mostly useful for debugging. In this journey I found out that there are some types that are simply "undetectable"... Like filters, procs/callables, etc.

Uses this in Varedit to make it slightly more clear which var is which. I played a lot with how it's displayed and it's kind of hard to make it visible enough but not distracting or taking up too much space. In the end I opted for a simple approach that just shows the short type and keeps varedit mostly untouched otherwise:
This commit is contained in:
Jiří Barouš
2020-11-10 08:13:18 +02:00
committed by GitHub
parent 44664fc90d
commit 2f370cad81
3 changed files with 97 additions and 5 deletions
+85
View File
@@ -891,6 +891,91 @@ var/list/wall_items = typecacheof(list(
return 1
return 0
// Returns a variable type as string, optionally with some details:
// Objects (datums) get their type, paths get the type name, scalars show length (text) and value (numbers), lists show length.
// Also attempts some detection of otherwise undetectable types using ref IDs
var/global/known_proc = new /proc/get_type_ref_bytes
/proc/get_debug_type(var/V, var/details = TRUE, var/print_numbers = TRUE, var/path_names = TRUE, var/text_lengths = TRUE, var/list_lengths = TRUE, var/show_useless_subtypes = TRUE)
// scalars / basic types
if(isnull(V))
return "null"
if(ispath(V))
return details && path_names ? "path([V])" : "path"
if(istext(V))
return details && text_lengths ? "text([length(V) ])" : "text"
if(isnum(V)) // Byond doesn't really differentiate between floats and ints, but we can sort of guess here
// also technically we could also say that 0 and 1 are boolean but that'd be quite silly
if(IsInteger(V) && V < 16777216 && V > -16777216)
return details && print_numbers ? "int([V])" : "int"
if(V >= INFINITY)
return details ? "float(+INF)" : "float"
if(V <= -INFINITY)
return details ? "float(-INF)" : "float"
return details && print_numbers ? "float([V])" : "float"
// Resource types
if(isicon(V))
return "icon"
if(isfile(V))
return "file"
// Types that don't inherit from /datum (note that /world is not here because you can't hold a reference to it)
if(islist(V))
return details && list_lengths ? "list([length(V)])" : "list"
if(isclient(V))
return "client"
if(istype(V, /savefile))
return "savefile"
// Finally actual objects that inherit from /datum
// We want to differentiate at least the basic "special" Byond types
var/datum/D = V
if(isarea(D))
return details ? "area([D.type])" : "area"
if(isturf(D))
return details ? "turf([D.type])" : "turf"
if(ismob(D))
return details ? "mob([D.type])" : "mob"
if(isobj(D))
return details ? "obj([D.type])" : "obj"
if(istype(D, /atom/movable)) // according to DM docs there should be no defined types under this but there certainly are some
return details ? "movable([D.type])" : "movable"
if(isatom(D))
return details ? "atom([D.type])" : "atom"
if(istype(D, /database))
return details && show_useless_subtypes ? "database([D.type])" : "database"
if(istype(D, /exception))
return details && show_useless_subtypes ? "exception([D.type])" : "exception"
if(istype(D, /mutable_appearance)) // must come before /image
return details && show_useless_subtypes ? "mutable_appearance([D.type])" : "mutable_appearance"
if(istype(D, /image))
return details ? "image([D.type])" : "image"
if(istype(D, /matrix))
return details && show_useless_subtypes ? "matrix([D.type])" : "matrix"
if(istype(D, /regex))
return details && show_useless_subtypes ? "regex([D.type])" : "regex"
if(istype(D, /sound))
return details ? "sound([D.type])" : "sound"
if(istype(D, /decl))
return details ? "decl([D.type])" : "decl"
if(isdatum(D))
return details ? "datum([D.type])" : "datum"
if(istype(D)) // let's future proof ourselves
return details ? "unknown-object([D.type])" : "unknown-object"
// some undetectable types
var/refType = get_type_ref_bytes(V)
if(refType == "")
return "unknown"
if(refType == get_type_ref_bytes(known_proc)) // it's a proc of some kind
if(istext(V?:name) && V:name != "") // procs with names are generally verbs
return "verb"
return "proc"
if(refType == "53")
return "filters"
if(refType == "3a")
return "appearance"
return "unknown-object([refType])" // If you see this you found a new undetectable type. Feel free to add it here.
/proc/get_type_ref_bytes(var/V) // returns first 4 bytes from \ref which denote the object type (for objects that is)
return lowertext(copytext(ref(V), 4, 6))
/proc/format_text(text)
return replacetext(replacetext(text,"\proper ",""),"\improper ","")
@@ -37,8 +37,9 @@
<title>[D] (\ref[D] - [D.type])</title>
<style>
body { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; font-size: 10pt; }
.key, .value { font-family: "Fira Code", Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, sans-serif; font-size: 9pt; }
.key, .type, .value { font-family: "Fira Code", Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, sans-serif; font-size: 9pt; }
.key { font-weight: bold }
.type { text-decoration: underline; color: gray }
</style>
</head>
<body onload='selectTextField(); updateSearch()'>
@@ -50,7 +51,7 @@
<td><div align='center'>[D.get_view_variables_header()]</div></td>
</tr></table>
<div align='center'>
<b><font size='1'>[replacetext("[D.type]", "/", "/<wbr>")]</font></b>
<b><font size='1'>[replacetext("[get_debug_type(D)]", "/", "/<wbr>")]</font></b>
[holder.marked_datum == D ? "<br/><font size='1' color='red'><b>Marked Object</b></font>" : ""]
</div>
</td>
@@ -124,10 +125,12 @@
/proc/make_view_variables_value(value, varname = "*")
var/vtext = ""
var/debug_type = get_debug_type(value, FALSE)
var/extra = list()
if(isnull(value))
vtext = "null"
// get_debug_type displays this
else if(istext(value))
debug_type = null // it's kinda annoying here; we can tell the type by the quotes
vtext = "\"[value]\""
else if(isicon(value))
vtext = "[value]"
@@ -144,7 +147,7 @@
vtext = "<a href='?_src_=vars;Vars=\ref[C]'>\ref[C]</a> - [C] ([C.type])"
else if(islist(value))
var/list/L = value
vtext = "/list ([L.len])"
vtext = "([L.len])"
if(!(varname in view_variables_dont_expand) && L.len > 0 && L.len < 100)
extra += "<ul>"
for (var/index = 1 to L.len)
@@ -157,7 +160,7 @@
else
vtext = "[value]"
return "<span class=value>[vtext]</span>[jointext(extra, "")]"
return "<span class=type>[debug_type]</span> <span class=value>[vtext]</span>[jointext(extra, "")]"
/proc/make_view_variables_var_entry(datum/D, varname, value, level=0)
var/ecm = null
+4
View File
@@ -0,0 +1,4 @@
author: Amunak
delete-after: True
changes:
- admin: "Slightly improves varedit by adding the variable type to the display."