From 721cce4b093a390c227c551842749273f0b5a2e8 Mon Sep 17 00:00:00 2001 From: Max Shawabkeh Date: Mon, 3 Jul 2017 04:36:56 -0700 Subject: [PATCH 1/5] Add a Lua server/client console debug tool. --- call_handlers/debugtools_commands.lua | 36 +++++ manifest.json | 17 ++- ui/lua_console/images/icon.png | Bin 0 -> 3391 bytes ui/lua_console/images/x-2x.png | Bin 0 -> 315 bytes ui/lua_console/lua_console.html | 36 +++++ ui/lua_console/lua_console.js | 185 ++++++++++++++++++++++++++ ui/lua_console/lua_console.less | 127 ++++++++++++++++++ 7 files changed, 398 insertions(+), 3 deletions(-) create mode 100644 ui/lua_console/images/icon.png create mode 100644 ui/lua_console/images/x-2x.png create mode 100644 ui/lua_console/lua_console.html create mode 100644 ui/lua_console/lua_console.js create mode 100644 ui/lua_console/lua_console.less diff --git a/call_handlers/debugtools_commands.lua b/call_handlers/debugtools_commands.lua index 1a21135..a84284b 100644 --- a/call_handlers/debugtools_commands.lua +++ b/call_handlers/debugtools_commands.lua @@ -657,4 +657,40 @@ function Commands:set_enable_animation_text_command(session, response, bool) radiant.util.set_global_config('enable_effect_triggers', bool) end +function Commands:exec_script_server(session, response, script, entity) + return self:exec_script(session, response, script, entity) +end + +function Commands:exec_script_client(session, response, script, entity) + return self:exec_script(session, response, script, entity) +end + +function Commands:exec_script(session, response, script, entity) + local expr, error + expr, error = loadstring('return ' .. script) + if not expr then + -- Maybe it's a statement? + expr, error = loadstring(script) + end + if error then + response:reject('ERROR: ' .. error) + end + + -- Set the entity as a global variable. This is terrible, but unavoidable if we want to + -- let executed code set global vars. We could instead compile the expression as a + -- function with an entity argument, but if it's a statement, it won't be able to + -- normally set global vars, so no REPL state. + local saved_entity = rawget(_G, 'entity') + rawset(_G, 'entity', entity) + local success, result = pcall(expr) + rawset(_G, 'entity', saved_entity) + + if success then + response:resolve(tostring(result == nil and '' or result)) + else + response:reject('ERROR: ' .. result) + end + return true +end + return Commands diff --git a/manifest.json b/manifest.json index ddd1afb..c1c72a7 100644 --- a/manifest.json +++ b/manifest.json @@ -25,7 +25,8 @@ "file(/ui/entity_editor/entity_editor.html)", "file(/ui/job_monitor/job_monitor.html)", "file(/ui/trace_monitor/trace_monitor.html)", - "file(/ui/item_dropper/item_dropper.html)" + "file(/ui/item_dropper/item_dropper.html)", + "file(/ui/lua_console/lua_console.html)" ], "js": [ "file(ui/debug_dock/debug_dock.js)", @@ -38,7 +39,8 @@ "file(/ui/entity_editor/entity_editor.js)", "file(/ui/job_monitor/job_monitor.js)", "file(/ui/trace_monitor/trace_monitor.js)", - "file(/ui/item_dropper/item_dropper.js)" + "file(/ui/item_dropper/item_dropper.js)", + "file(/ui/lua_console/lua_console.js)" ], "less": [ "file(/html/font-awesome-4.3.0/css/font-awesome.min.css)", @@ -51,7 +53,8 @@ "file(/ui/entity_editor/entity_editor.less)", "file(/ui/job_monitor/job_monitor.less)", "file(/ui/trace_monitor/trace_monitor.less)", - "file(/ui/item_dropper/item_dropper.less)" + "file(/ui/item_dropper/item_dropper.less)", + "file(/ui/lua_console/lua_console.less)" ] }, "functions": { @@ -266,6 +269,14 @@ "set_enable_animation_text_command": { "controller": "file(call_handlers/debugtools_commands.lua)", "endpoint": "server" + }, + "exec_script_server": { + "controller": "file(call_handlers/debugtools_commands.lua)", + "endpoint": "server" + }, + "exec_script_client": { + "controller": "file(call_handlers/debugtools_commands.lua)", + "endpoint": "client" } }, "default_locale": "en" diff --git a/ui/lua_console/images/icon.png b/ui/lua_console/images/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c49eab4f11c05aa612f2f16b399e067a8a00eda0 GIT binary patch literal 3391 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 VK!AZ@PWu1=002ovPDHLkV1k}4U*-S+ literal 0 HcmV?d00001 diff --git a/ui/lua_console/images/x-2x.png b/ui/lua_console/images/x-2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ae2ab829004738124be21ab88a5b952f785e372 GIT binary patch literal 315 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ za0`PlBg3pY5Pvx(C*`!Io_ln*!s91Gi z;pQoG^rKIw$zB(=Fxj|wV|4Myf6fIfd=roEyvesC|C9B1X5|aj8WBI&R{%ZA;OXk; Jvd$@?2>@5^gF^rS literal 0 HcmV?d00001 diff --git a/ui/lua_console/lua_console.html b/ui/lua_console/lua_console.html new file mode 100644 index 0000000..df826c0 --- /dev/null +++ b/ui/lua_console/lua_console.html @@ -0,0 +1,36 @@ + + + + + + diff --git a/ui/lua_console/lua_console.js b/ui/lua_console/lua_console.js new file mode 100644 index 0000000..e0fbf35 --- /dev/null +++ b/ui/lua_console/lua_console.js @@ -0,0 +1,185 @@ +var topElement; +$(document).on('stonehearthReady', function(){ + topElement = $(top); + App.debugDock.addToDock(App.StonehearthLuaConsoleIcon); +}); + +App.StonehearthLuaConsoleIcon = App.View.extend({ + templateName: 'luaConsoleIcon', + classNames: ['debugDockIcon'], + + didInsertElement: function() { + $('#luaConsoleIcon').tooltipster(); + this.$().click(function () { + var view = App.debugView.getView(App.StonehearthLuaConsoleView); + if (view) { + view.$().toggle(); + if (view.$().is(':visible')) { + view.focus(); + } + } else { + App.debugView.addView(App.StonehearthLuaConsoleView) + } + }); + } +}); + +App.StonehearthLuaConsoleView = App.View.extend({ + templateName: 'luaConsole', + uriProperty: 'model', + envContainer: Ember.ContainerView.extend(), + + init: function() { + this._super(); + }, + + didInsertElement: function() { + var self = this; + + this.$().draggable(); + + this.$('.button').tooltipster({ + theme: 'tooltipster-shdt', + arrow: true, + }); + + this.$("#closeConsoleButton").click(function () { + self.$().hide(); + }); + + self.clientEnv = App.StonehearthLuaConsoleEnvironmentView.create({ env: 'client' }); + self.serverEnv = App.StonehearthLuaConsoleEnvironmentView.create({ env: 'server' }); + + self.get('envContainerInstance').pushObject(self.serverEnv); + self.get('envContainerInstance').pushObject(self.clientEnv); + + setTimeout(function () { self.switchEnv(false); }, 1); + }, + + destroy: function() { + topElement.off("radiant_selection_changed.object_browser"); + this._super(); + }, + + switchEnv: function (isClient) { + var toShow = isClient ? this.clientEnv : this.serverEnv; + var toHide = isClient ? this.serverEnv : this.clientEnv; + + toHide.$().hide(); + toShow.$().show(); + toShow.focus(); + + var toSelect = isClient ? '#clientButton' : '#serverButton'; + var toDeselect = isClient ? '#serverButton' : '#clientButton'; + this.$(toSelect).addClass('selected'); + this.$(toDeselect).removeClass('selected'); + }, + + focus: function () { + if (this.clientEnv.$().is(':visible')) { + this.clientEnv.focus(); + } else { + this.serverEnv.focus(); + } + }, + + actions: { + switchToServer: function () { + this.switchEnv(false); + }, + + switchToClient: function () { + this.switchEnv(true); + } + } +}); + +App.StonehearthLuaConsoleEnvironmentView = App.View.extend({ + templateName: 'stonehearthLuaConsoleEnvironment', + uriProperty: 'model', + env: null, + + init: function () { + this._super(); + this._history = []; + this._curHistoryIdx = 0; + this._curCommand = ''; + this.currentEntity = null; + }, + + didInsertElement: function() { + var self = this; + + topElement.on("radiant_selection_changed.object_browser", function (_, data) { + self.currentEntity = App.stonehearthClient.getSubSelectedEntity(); + if (!self.currentEntity) { + self.currentEntity = App.stonehearthClient.getSelectedEntity(); + } + }); + + self.$('#input').keydown(function(e) { + if (e.keyCode == 13 && !e.originalEvent.repeat) { // Enter + var command = $(this).val(); + if (!command) return; + self._history.push(command); + $(this).val(''); + self._curHistoryIdx = self._history.length - 1; + + var outputArea = self.$('.output'); + outputArea.append($('
').text('> ' + command)); + + var resultContainer = $('
').text('...'); + outputArea.append(resultContainer); + + radiant.call('debugtools:exec_script_' + self.env, command, self.currentEntity) + .done(function (o) { + resultContainer.removeClass('progress').text(o.result); + }) + .fail(function (o) { + if (typeof o.error == 'string') { + // Client failures don't decode. We should fix it at the root, but this hack here is fine for now. + o = JSON.parse(o.error) + } + resultContainer.removeClass('progress').addClass('error').text(o.result); + }); + + outputArea.scrollTop(outputArea[0].scrollHeight); + } + }); + + this.$('#input').keydown(function (e) { + if (e.keyCode == 38) { // UpArrow + if (self._curHistoryIdx == self._history.length - 1) { + // Remember our current command, if any. + if ($(this).val() != '') { + self._curCommand = $(this).val(); + } + } + + $(this).val(self._history[self._curHistoryIdx]); + + self._curHistoryIdx--; + if (self._curHistoryIdx < 0) { + self._curHistoryIdx = 0; + } + e.preventDefault(); + } else if (e.keyCode == 40) { // DownArrow + if (self._curHistoryIdx == self._history.length - 1) { + if (self._curCommand != '') { + $(this).val(self._curCommand); + self._curCommand = ''; + } + return; + } + self._curHistoryIdx++; + $(this).val(self._history[self._curHistoryIdx]); + e.preventDefault(); + } + }); + }, + + focus: function () { + this.$('#input').focus(); + }, +}); + diff --git a/ui/lua_console/lua_console.less b/ui/lua_console/lua_console.less new file mode 100644 index 0000000..ec2aa0b --- /dev/null +++ b/ui/lua_console/lua_console.less @@ -0,0 +1,127 @@ +#luaConsoleIcon { + .debugDockIcon; + background: url('./images/icon.png'); +} + +#luaConsole { + .debug; + position: absolute; + top: 10px; + left: 420px; + width: 1000px; + font-family: "Consolas", monospace; + padding: 0; + + .header { + margin-bottom: 1px; + font-size: 125%; + + #closeConsoleButton { + float: right; + width: 16px; + height: 16px; + padding: 1px; + background: url(images/x-2x.png) no-repeat center; + } + } + + #toolbar { + display: inline-block; + } + + .button { + position: relative; + color: #fff; + margin-left: 30px; + font-weight: bold; + + &:hover { + color: #fc0; + } + } + + .selected { + color: #fc0; + } + + #close { + position: absolute; + top: 8px; + right: 4px; + } + + .title { + font-size: 16px; + color: white; + } + .subtitle { + font-size: 12px; + color: white; + } + + .menu { + li { + margin-left: 0px; + padding-left: 0px; + list-style-type: none; + i { + color: #fc0; + } + &:hover { + color: #fc0; + } + } + } +} + +.tooltipster-shdt { + .tooltipster-content { + .debug; + } +} + +#stonehearthLuaConsoleEnvironment { + .output { + -webkit-user-select: all; + height: 250px; + overflow: auto; + resize: vertical; + font-size: 110%; + + #title { + padding: 6px; + background: #3d3f36; + } + + .cmdIn { + font-weight: bold; + } + + .cmdOut { + font-weight: bold; + margin-bottom: 4px; + margin-left: 14px; + color: green; + &.progress { + color: yellow; + } + &.error { + color: #cc0000; + } + } + } + + .input { + width: 100%; + + input { + width: 100%; + left: 4px; + right: 4px; + border: 0px; + background: #000; + color: #ffc000; + padding: 4px; + } + } +} From 90ee5d5e756380aa9a281a2555281633ba448271 Mon Sep 17 00:00:00 2001 From: Max Shawabkeh Date: Mon, 3 Jul 2017 15:34:06 -0700 Subject: [PATCH 2/5] Lua console visual/UX polish. --- call_handlers/debugtools_commands.lua | 18 +++++++-- ui/lua_console/images/icon.png | Bin 3391 -> 3421 bytes ui/lua_console/images/resizer.png | Bin 0 -> 244 bytes ui/lua_console/images/x-2x.png | Bin 315 -> 375 bytes ui/lua_console/lua_console.html | 4 +- ui/lua_console/lua_console.js | 35 ++++++---------- ui/lua_console/lua_console.less | 55 +++++++++++++++++--------- 7 files changed, 66 insertions(+), 46 deletions(-) create mode 100644 ui/lua_console/images/resizer.png diff --git a/call_handlers/debugtools_commands.lua b/call_handlers/debugtools_commands.lua index a84284b..ef1349a 100644 --- a/call_handlers/debugtools_commands.lua +++ b/call_handlers/debugtools_commands.lua @@ -680,13 +680,23 @@ function Commands:exec_script(session, response, script, entity) -- let executed code set global vars. We could instead compile the expression as a -- function with an entity argument, but if it's a statement, it won't be able to -- normally set global vars, so no REPL state. - local saved_entity = rawget(_G, 'entity') - rawset(_G, 'entity', entity) + local saved_entity = rawget(_G, 'e') + rawset(_G, 'e', entity) local success, result = pcall(expr) - rawset(_G, 'entity', saved_entity) + rawset(_G, 'e', saved_entity) if success then - response:resolve(tostring(result == nil and '' or result)) + local output + if result == nil then + output = '' + elseif type(result) == 'string' then + output = string.gsub(string.format('%q', result), '\n', 'n') + elseif type(result) == 'table' then + output = radiant.util.table_tostring(result, 40, 0) + else + output = tostring(result) + end + response:resolve(output) else response:reject('ERROR: ' .. result) end diff --git a/ui/lua_console/images/icon.png b/ui/lua_console/images/icon.png index c49eab4f11c05aa612f2f16b399e067a8a00eda0..e17a2e41a3e94f668810d98186623d860731a48a 100644 GIT binary patch delta 666 zcmV;L0%iTb8r>SOtqKYe000XS0e@s)k(0LyD1T{5L_t(oN5$5?PZMz*25?Lq7&^E~ z!s5U{p!E6ziE;7|ATBx*Xa596G?4~1G7v@MYYOoV5e5?n2PY?s)t1t#gHAFSHSr}5 zM9_YHzRlg`?$_r^sn>J(kh^a$&(k}8cfBqV`LF0ON^}?{IvhNa?`QK)Xc(Y;5(I8S zkAL}j*nB%E?1ew}7f|2fIPCTw7bf2hiq6`_rgt#vJvJ=v1`2xMr(GE2352>n+zkZl z|D2VYwqOSKc!S#IZfGFP+6AoG^aYN=Kdq030RsEsDX>ze1P9HWR?(qC=qTvoW2MXj z>|6Mq8r4w(a1%B;f}%QDen5QGlHbGxJ!2VfV= za?s&+5IP2*?UEq+M6C^s=d&!sL5IyCv=5f-l0X9|^B+>V^5V#&H|r2%S&9xBgvKBX z8%{}(#PmzaELJyAyaatL&q0SKgz6>#=q+J}&J`qcw<78Ll~3tAYlm2ti4GkI-G2|; zF1q;S3rWqD>t~(3zIvWz1))QPwszZ{UXkRDRmnW8{!ZO0=Ww}q{8;wwAXFbb6VUuu z@zjb~mrF9d{JMsP_6%pg*PF8w?goO7K+{JrZYfF3ywXdpm;8O=VsXS9){goggzFCw zEp-fCz{*~zO8WNN59?C#s2S8QcYgyV^Jb}vPZhLO()TJ`IAeXzhx&TvZXj3}-lki8 z1}i&K$ArzdgENj{fo&$QmcGWb zg~Q&@I;CNN;z3-Aj=hGP=rBrj7$rK4j&%J6YYHW5KurH)00000NkvXXt^-0~f}O@b A=l}o! delta 635 zcmV->0)+kD8owH_tqKYf000XT0n*)m{FAo|D1S>yL_t(oN5$4XYZOrw#_^rm5mIbn zXTZiHMZm&FL9F}$!A8qq?MJY%%4!he8ww&QCMp<141&=_T}8yg($YdJQ-}(_U=d7o zkN+bxFnfJwGyB3={0MV$=Xo-dJF{Cev;RsCt0ae2lEW&=VcxbW8&~sTk;4q-Q!re` zsDD~BC^>W#?!@o(4{+ab9En=rFFAA+O{c}7x0q1p^g|9Q7{!mYu*fO~-XC&Eu>a3h zX=oGYv0deqA%`A_OKE{B4t>Tk^t?ahFhk%tT2Q6RYaCQrMdUC<=mdQ8sZwPgyHr+4 z4iy4N;E&-~sM63+%wR<2W#q6M0{sb@g?}nl)-b8EoE(-x=w7@^OUgKaD9q_N=m(*b z_>`6u@i%HaX<9QpzvG}+gpOe`Eh&_owD-}HE!1Ax8XEbGj=-`AZJ>$utfWZ&3MaH| zo#I7|g;^a3Wf1DO{6oG(sb4gU9&*AX`xrg2Ln_P3p+e|>Y`N)@GZf$B1g<*1V}GYr zR-PQ@5nAf8OQ?z#IN_=NitgHVZg;8IvK%@>{l#+vT|X6*X|wnyCp@<{8|?*GzeD8} zkwb!ypzEubN6O-BEN=b1krgL16fg0MC)SQ{*fO)ezkr0~&{1;AcXm9_ z!83l|vNy?D+oRSFLJl2Q++c%k#JBBBa?TE`HNzl>8H#svCx-epJS2xzlEdm)?GFH> VK!AZ@PWu1=002ovPDHLkV1ni7Fq8lQ diff --git a/ui/lua_console/images/resizer.png b/ui/lua_console/images/resizer.png new file mode 100644 index 0000000000000000000000000000000000000000..31fa05f264dd03c20103a941b3eec2931cf740c6 GIT binary patch literal 244 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwr2>%=KS^ic&mX978Ppm-aDoF(`7d{Qkdu?GZ8l(>eK$L4|En(t;NkeU3ITs)#f2 zjAED}sIi7uks;?|e8}e3)lCfRw<}p3+ML&(n{i-~D~_k=dl| h$@854oBZ!p*D^1$I%uzY)DUDJgQu&X%Q~loCIC+=PyPS^ literal 0 HcmV?d00001 diff --git a/ui/lua_console/images/x-2x.png b/ui/lua_console/images/x-2x.png index 6ae2ab829004738124be21ab88a5b952f785e372..5900283c4587191408b2806f949311a20f834be5 100644 GIT binary patch delta 348 zcmV-i0i*uA0`~%tB!2;OQb$4nuFf3k00004XF*Lt006O%3;baP00009a7bBm000XT z000XT0n*)m`~Uy|8FWQhbW?9;ba!ELWdLwtX>N2bZe?^JG%heMF*ZbLuk`=`0Ov_W zK~y+Tg_21M0znK#8}T5XM^F$P5XFVywH$Ea!b`dE5RT}7MSrJBx2g8STT~@K2~{ad zb;U`H(;yc<@7M;zcSiX*(D!wS<1e3$0dxPOSoODcA~=DY&O`Nj;+ zQ*v%{)r0S|euo9dkuXhAo^XSQU2te#Fe#lP9J)-&|AIr;1Msp0H--E0fbk)YYRL_5 zE1%!t6Q_THm-xtPUzp@@z9ju0c9N3&y%pLpc&fmmvy_}KTgzr0*A+N4vAwZdbMgK| maHvgT#ktuIxEXKqJNE-4(0eFY`LB2Y0000 -
+
@@ -27,7 +27,7 @@ -