-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDevTools.lua
More file actions
362 lines (362 loc) · 10.5 KB
/
DevTools.lua
File metadata and controls
362 lines (362 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
------------------------------------------------------------------------------
-- DevToolsDump.lua
--
-- /dump Implementation
--
-- Globals: DevTools, SLASH_DEVTOOLSDUMP1, DevTools_Dump, DevTools_RunDump
-- Globals: DEVTOOLS_MAX_ENTRY_CUTOFF, DEVTOOLS_LONG_STRING_CUTOFF
-- Globals: DEVTOOLS_DEPTH_CUTOFF, DEVTOOLS_INDENT
-- Globals: DEVTOOLS_USE_TABLE_CACHE, DEVTOOLS_USE_FUNCTION_CACHE
-- Globals: DEVTOOLS_USE_USERDATA_CACHE
---------------------------------------------------------------------------
local DT = { utils = {} };
DEVTOOLS_MAX_ENTRY_CUTOFF = 50; -- Maximum table entries shown
DEVTOOLS_LONG_STRING_CUTOFF = 200; -- Maximum string size shown
DEVTOOLS_DEPTH_CUTOFF = 10; -- Maximum table depth
DEVTOOLS_USE_TABLE_CACHE = true; -- Look up table names
DEVTOOLS_USE_FUNCTION_CACHE = true;-- Look up function names
DEVTOOLS_USE_USERDATA_CACHE = true;-- Look up userdata names
DEVTOOLS_INDENT=' '; -- Indentation string
local DEVTOOLS_TYPE_COLOR="|cff88ff88";
local DEVTOOLS_TABLEREF_COLOR="|cffffcc00";
local DEVTOOLS_CUTOFF_COLOR="|cffff0000";
local DEVTOOLS_TABLEKEY_COLOR="|cff88ccff";
local FORMATS = {};
-- prefix type suffix
FORMATS["opaqueTypeVal"] = "%s" .. DEVTOOLS_TYPE_COLOR .. "<%s>|r%s";
-- prefix type name suffix
FORMATS["opaqueTypeValName"] = "%s" .. DEVTOOLS_TYPE_COLOR .. "<%s %s>|r%s";
-- type
FORMATS["opaqueTypeKey"] = "<%s>";
-- type name
FORMATS["opaqueTypeKeyName"] = "<%s %s>";
-- value
FORMATS["bracketTableKey"] = "[%s]";
-- prefix value
FORMATS["tableKeyAssignPrefix"] = DEVTOOLS_TABLEKEY_COLOR .. "%s%s|r=";
-- prefix cutoff
FORMATS["tableEntriesSkipped"] = "%s" .. DEVTOOLS_CUTOFF_COLOR .. "<skipped %s>|r";
-- prefix suffix
FORMATS["tableTooDeep"] = "%s" .. DEVTOOLS_CUTOFF_COLOR .. "<table (too deep)>|r%s";
-- prefix value suffix
FORMATS["simpleValue"] = "%s%s%s";
-- prefix tablename suffix
FORMATS["tableReference"] = "%s" .. DEVTOOLS_TABLEREF_COLOR .. "%s|r%s";
-- Grab a copy various oft-used functions
local rawget = rawget;
local type = type;
local string_len = string.len;
local string_sub = string.sub;
local string_gsub = string.gsub;
local string_format = string.format;
local string_match = string.match;
local function WriteMessage(msg)
DEFAULT_CHAT_FRAME:AddMessage(msg);
end
local function prepSimple(val, context)
local valType = type(val);
if (valType == "nil") then
return "nil";
elseif (valType == "number") then
return val;
elseif (valType == "boolean") then
if (val) then
return "true";
else
return "false";
end
elseif (valType == "string") then
local l = string_len(val);
if ((l > DEVTOOLS_LONG_STRING_CUTOFF) and
(DEVTOOLS_LONG_STRING_CUTOFF > 0)) then
local more = l - DEVTOOLS_LONG_STRING_CUTOFF;
val = string_sub(val, 1, DEVTOOLS_LONG_STRING_CUTOFF);
return string_gsub(string_format("%q...+%s",val,more),"[|]", "||");
else
return string_gsub(string_format("%q",val),"[|]", "||");
end
elseif (valType == "function") then
local fName = context:GetFunctionName(val);
if (fName) then
return string_format(FORMATS.opaqueTypeKeyName, valType, fName);
else
return string_format(FORMATS.opaqueTypeKey, valType);
end
return string_format(FORMATS.opaqueTypeKey, valType);
elseif (valType == "userdata") then
local uName = context:GetUserdataName(val);
if (uName) then
return string_format(FORMATS.opaqueTypeKeyName, valType, uName);
else
return string_format(FORMATS.opaqueTypeKey, valType);
end
elseif (valType == 'table') then
local tName = context:GetTableName(val);
if (tName) then
return string_format(FORMATS.opaqueTypeKeyName, valType, tName);
else
return string_format(FORMATS.opaqueTypeKey, valType);
end
end
error("Bad type '" .. valType .. "' to prepSimple");
end
local function prepSimpleKey(val, context)
local valType = type(val);
if (valType == "string") then
local l = string_len(val);
if ((l <= DEVTOOLS_LONG_STRING_CUTOFF) or
(DEVTOOLS_LONG_STRING_CUTOFF <= 0)) then
if (string.gfind(val, "^[a-zA-Z_][a-zA-Z0-9_]*$")) then
return val;
end
end
end
return string_format(FORMATS.bracketTableKey, prepSimple(val, context));
end
local function DevTools_InitFunctionCache(context)
local ret = {};
for _,k in ipairs(DT.functionSymbols) do
local v = getglobal(k);
if (type(v) == 'function') then
ret[v] = '[' .. k .. ']';
end
end
for k,v in pairs(getfenv(0)) do
if (type(v) == 'function') then
if (not ret[v]) then
ret[v] = '[' .. k .. ']';
end
end
end
return ret;
end
local function DevTools_InitUserdataCache(context)
local ret = {};
for _,k in ipairs(DT.userdataSymbols) do
local v = getglobal(k);
if (type(v) == 'table') then
local u = rawget(v,0);
if (type(u) == 'userdata') then
ret[u] = k .. '[0]';
end
end
end
for k,v in pairs(getfenv(0)) do
if (type(v) == 'table') then
local u = rawget(v, 0);
if (type(u) == 'userdata') then
if (not ret[u]) then
ret[u] = k .. '[0]';
end
end
end
end
return ret;
end
local function DevTools_Cache_Nil(self, value, newName)
return nil;
end
local function DevTools_Cache_Function(self, value, newName)
if (not self.fCache) then
self.fCache = DevTools_InitFunctionCache(self);
end
local name = self.fCache[value];
if ((not name) and newName) then
self.fCache[value] = newName;
end
return name;
end
local function DevTools_Cache_Userdata(self, value, newName)
if (not self.uCache) then
self.uCache = DevTools_InitUserdataCache(self);
end
local name = self.uCache[value];
if ((not name) and newName) then
self.uCache[value] = newName;
end
return name;
end
local function DevTools_Cache_Table(self, value, newName)
if (not self.tCache) then
self.tCache = {};
end
local name = self.tCache[value];
if ((not name) and newName) then
self.tCache[value] = newName;
end
return name;
end
local function DevTools_Write(self, msg)
DEFAULT_CHAT_FRAME:AddMessage(msg);
end
local DevTools_DumpValue;
local function DevTools_DumpTableContents(val, prefix, firstPrefix, context)
local showCount = 0;
local oldDepth = context.depth;
local oldKey = context.key;
-- Use this to set the cache name
context:GetTableName(val, oldKey or 'value');
local iter = pairs(val);
local nextK, nextV = iter(val, nil);
while (nextK) do
local k,v = nextK, nextV;
nextK, nextV = iter(val, k);
showCount = showCount + 1;
if ((showCount <= DEVTOOLS_MAX_ENTRY_CUTOFF) or
(DEVTOOLS_MAX_ENTRY_CUTOFF <= 0)) then
local prepKey = prepSimpleKey(k, context);
if (oldKey == nil) then
context.key = prepKey;
elseif (string_sub(prepKey, 1, 1) == "[") then
context.key = oldKey .. prepKey
else
context.key = oldKey .. "." .. prepKey
end
context.depth = oldDepth + 1;
local rp = string_format(FORMATS.tableKeyAssignPrefix, firstPrefix,
prepKey);
firstPrefix = prefix;
DevTools_DumpValue(v, prefix, rp,
(nextK and ",") or '',
context);
end
end
local cutoff = showCount - DEVTOOLS_MAX_ENTRY_CUTOFF;
if ((cutoff > 0) and (DEVTOOLS_MAX_ENTRY_CUTOFF > 0)) then
context:Write(string_format(FORMATS.tableEntriesSkipped,firstPrefix,
cutoff));
end
context.key = oldKey;
context.depth = oldDepth;
return (showCount > 0)
end
-- Return the specified value
function DevTools_DumpValue(val, prefix, firstPrefix, suffix, context)
local valType = type(val);
if (valType == "userdata") then
local uName = context:GetUserdataName(val, 'value');
if (uName) then
context:Write(string_format(FORMATS.opaqueTypeValName,
firstPrefix, valType, uName, suffix));
else
context:Write(string_format(FORMATS.opaqueTypeVal,
firstPrefix, valType, suffix));
end
return;
elseif (valType == "function") then
local fName = context:GetFunctionName(val, 'value');
if (fName) then
context:Write(string_format(FORMATS.opaqueTypeValName,
firstPrefix, valType, fName, suffix));
else
context:Write(string_format(FORMATS.opaqueTypeVal,
firstPrefix, valType, suffix));
end
return;
elseif (valType ~= "table") then
context:Write(string_format(FORMATS.simpleValue,
firstPrefix,prepSimple(val, context),
suffix));
return;
end
local cacheName = context:GetTableName(val);
if (cacheName) then
context:Write(string_format(FORMATS.tableReference,
firstPrefix, cacheName, suffix));
return;
end
if ((context.depth >= DEVTOOLS_DEPTH_CUTOFF) and
(DEVTOOLS_DEPTH_CUTOFF > 0)) then
context:Write(string_format(FORMATS.tableTooDeep,
firstPrefix, suffix));
return;
end
firstPrefix = firstPrefix .. "{";
local oldPrefix = prefix;
prefix = prefix .. DEVTOOLS_INDENT;
context:Write(firstPrefix);
firstPrefix = prefix;
local anyContents = DevTools_DumpTableContents(val, prefix, firstPrefix,
context);
context:Write(oldPrefix .. "}" .. suffix);
end
local function Pick_Cache_Function(func, setting)
if (setting) then
return func;
else
return DevTools_Cache_Nil;
end
end
function DevTools_RunDump(value, context)
local prefix = "";
local firstPrefix = prefix;
local valType = type(value);
if (type(value) == 'table') then
local any =
DevTools_DumpTableContents(value, prefix, firstPrefix, context);
if (context.Result) then
return context:Result();
end
-- if (not any) then
-- context:Write("empty result");
-- end
return;
end
DevTools_DumpValue(value, '', '', '', context);
if (context.Result) then
return context:Result();
end
end
-- Dump the specified list of value
function DevTools_Dump(value, startKey)
local context = {
depth = 0,
key = startKey,
};
context.GetTableName = Pick_Cache_Function(DevTools_Cache_Table,
DEVTOOLS_USE_TABLE_CACHE);
context.GetFunctionName = Pick_Cache_Function(DevTools_Cache_Function,
DEVTOOLS_USE_FUNCTION_CACHE);
context.GetUserdataName = Pick_Cache_Function(DevTools_Cache_Userdata,
DEVTOOLS_USE_USERDATA_CACHE);
context.Write = DevTools_Write;
DevTools_RunDump(value, context);
end
local function DevTools_DumpCommand(msg, editBox)
if (string.gfind(msg,"^[A-Za-z_][A-Za-z0-9_]*$")) then
-- WriteMessage("DevTools: " .. msg);
local val = getglobal(msg);
local tmp = {};
if (val == nil) then
local key = string_format(FORMATS.tableKeyAssignPrefix,
'', prepSimpleKey(msg, {}));
WriteMessage(key .. "nil,");
else
tmp[msg] = val;
end
DevTools_Dump(tmp);
return;
end
WriteMessage("DevTools: value=" .. msg);
local func,err = loadstring("return " .. msg);
if (not func) then
WriteMessage("DevTools: ERROR: " .. err);
else
DevTools_Dump({ func() }, "value");
end
end
SLASH_DEVTOOLSDUMP1 = "/dump";
SlashCmdList["DEVTOOLSDUMP"] = DevTools_DumpCommand;
DT.functionSymbols = {};
DT.userdataSymbols = {};
local funcSyms = DT.functionSymbols;
local userSyms = DT.userdataSymbols;
for k,v in pairs(getfenv(0)) do
if (type(v) == 'function') then
table.insert(funcSyms, k);
elseif (type(v) == 'table') then
if (type(rawget(v,0)) == 'userdata') then
table.insert(userSyms, k);
end
end
end