init.lua 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. --[[
  2. Debug console class
  3. @author : Eiyeron Fulmincendii
  4. A Quake-like console to display a log and input some registerable commands.
  5. Note that the log can be filtered by verbosity, from INFO to ERROR.
  6. To register a command, call register command
  7. debug_overlay.console:registerCommand("my function", function(<arguments>) end, "This command does ...")
  8. The help string is optional but is useful for the help/ls command.
  9. A few commands are already built-in, please check commands.lua to see them.
  10. ]]--
  11. require("imgui")
  12. local class = require("class")
  13. local DebugWindow = require("debug_overlay.debugwindow")
  14. local commands = require("debug_overlay.console.commands")
  15. local Console = class(DebugWindow)
  16. local INFO = 1
  17. local DEBUG = 2
  18. local LOG = 3
  19. local WARN = 4
  20. local ERROR = 5
  21. local function bind(new_self, fun, ...)
  22. return function(...)
  23. fun(new_self, ...)
  24. end
  25. end
  26. local function splitArgs(text)
  27. local result = {}
  28. local e = 0
  29. while true do
  30. local b = e+1
  31. b = text:find("%S",b)
  32. if b==nil then break end
  33. if text:sub(b,b)=="'" then
  34. e = text:find("'",b+1)
  35. b = b+1
  36. elseif text:sub(b,b)=='"' then
  37. e = text:find('"',b+1)
  38. b = b+1
  39. else
  40. e = text:find("%s",b+1)
  41. end
  42. if e==nil then e=#text+1 end
  43. result[#result+1] = text:sub(b,e-1)
  44. end
  45. return result
  46. end
  47. function Console:init()
  48. self.shortcut = "f12"
  49. self.logs = {}
  50. self.logs_filter_toggles = {}
  51. self.visible = false;
  52. self.input_text = "";
  53. self.commands = {}
  54. self.command_helps = {}
  55. self.take_focus = false
  56. self.scroll_to_bottom = true
  57. for i=1,5 do
  58. self.logs_filter_toggles[i] = true
  59. end
  60. for i,v in pairs(commands) do
  61. self:registerCommand(i, bind(self, commands[i].fun), v.help)
  62. end
  63. end
  64. function Console:render()
  65. imgui.SetNextWindowPos(0, love.graphics.getHeight() - 200, 1);
  66. imgui.SetNextWindowSize(love.graphics.getWidth(), 200);
  67. imgui.Begin("Console", nil, {"NoCollapse", "NoResize", "NoMove", "NoTitleBar"})
  68. if imgui.Button("Bottom") then self.scroll_to_bottom = true end
  69. for k,v in ipairs({"Info", "Debug", "Log", "Warn", "Error"}) do
  70. imgui.SameLine()
  71. if imgui.Checkbox(v, self.logs_filter_toggles[k]) then
  72. self.logs_filter_toggles[k] = not self.logs_filter_toggles[k]
  73. end
  74. end
  75. imgui.BeginChild("Console log", 0,-imgui.GetItemsLineHeightWithSpacing())
  76. for k,l in ipairs(self.logs) do
  77. local color = 0xFFFFFF
  78. if self.logs_filter_toggles[l.level] then
  79. if l.level == 1 then
  80. imgui.PushStyleColor("Text", 0.6,0.6,0.6,1)
  81. elseif l.level == 2 then
  82. imgui.PushStyleColor("Text", 0.4,0.4,0.6,1)
  83. elseif l.level == 3 then
  84. imgui.PushStyleColor("Text", 1,1,1,1)
  85. elseif l.level == 4 then
  86. imgui.PushStyleColor("Text", 1,1,0,1)
  87. elseif l.level == 5 then
  88. imgui.PushStyleColor("Text", 1,0.2,0.2,1)
  89. end
  90. imgui.TextUnformatted(l.msg)
  91. imgui.PopStyleColor(1)
  92. end
  93. end
  94. if self.scroll_to_bottom then
  95. imgui.SetScrollHere();
  96. self.scroll_to_bottom = false;
  97. end
  98. imgui.EndChild()
  99. local status = false
  100. imgui.PushItemWidth(imgui.GetContentRegionAvailWidth())
  101. status, self.input_text = imgui.InputText("", self.input_text, 256, {"EnterReturnsTrue"})
  102. imgui.PopItemWidth()
  103. if self.take_focus then
  104. imgui.SetKeyboardFocusHere(0)
  105. self.take_focus = false
  106. end
  107. if status then
  108. if self.input_text == "" then return end
  109. local command_line = self.input_text
  110. local parts = splitArgs(command_line)
  111. local args = {}
  112. for i=2,#parts do
  113. args[i-1] = parts[i]
  114. end
  115. self:log("> "..self.input_text, 1)
  116. local fun_name = parts[1]:lower()
  117. local command = self.commands[fun_name]
  118. if not command then
  119. self:log("Command not found : "..fun_name, ERROR)
  120. else
  121. local processed_commands = {}
  122. local f = function()
  123. command(unpack(args))
  124. end
  125. local success, result = xpcall(f, function(err) return debug.traceback(err) end)
  126. if not success then
  127. self:log(result, ERROR)
  128. end
  129. end
  130. self.input_text = ""
  131. self.take_focus = true
  132. end
  133. imgui.End()
  134. end
  135. function Console:toggle()
  136. DebugWindow.toggle(self)
  137. if self.visible then
  138. self.take_focus = true
  139. end
  140. end
  141. function Console:log(str, level)
  142. self.logs[#self.logs + 1] = {
  143. msg = str,
  144. level = level or 3
  145. }
  146. self.scroll_to_bottom = true
  147. end
  148. function Console:info(str)
  149. self:log(str, INFO)
  150. end
  151. function Console:warn(str)
  152. self:log(str, WARN)
  153. end
  154. function Console:debug(str)
  155. self:log(str, DEBUG)
  156. end
  157. function Console:error(str)
  158. self:log(str, ERROR)
  159. end
  160. function Console:registerCommand(name, fun, help)
  161. self.commands[name] = fun
  162. if help then
  163. self.command_helps[name] = help
  164. end
  165. end
  166. return Console