diff --git a/lua/wire/client/e2helper.lua b/lua/wire/client/e2helper.lua index f1ef16f60d..9e7ea16615 100644 --- a/lua/wire/client/e2helper.lua +++ b/lua/wire/client/e2helper.lua @@ -1,7 +1,7 @@ --[[ Expression 2 Helper for Expression 2 -HP- (and tomylobo, though he breaks a lot ^^) - Divran made CPU support + Divran made the original CPU support Fasteroid made the "from" column ]] -- @@ -12,50 +12,53 @@ E2Helper.Descriptions = {} include("e2descriptions.lua") ------------------------------- ----- CPU support -E2Helper.CPUDescriptions = {} -E2Helper.CPUTable = {} -E2Helper.CurrentMode = true -- E2/CPU. True = E2, false = CPU - -local function AddCPUDesc(FuncName, Args, Desc, Platform, Type) - table.insert(E2Helper.CPUTable, { [1] = FuncName, [2] = Args, [3] = Platform, [4] = Type }) - E2Helper.CPUDescriptions[FuncName] = Desc -end - -if CPULib then - -- Add help on all opcodes - for _, instruction in ipairs(CPULib.InstructionTable) do - if (instruction.Mnemonic ~= "RESERVED") and - (not instruction.Obsolete) then - local instructionArgs = instruction.Operand1 - if instruction.Operand2 ~= "" then - instructionArgs = instructionArgs .. ", " .. instruction.Operand2 - end - - AddCPUDesc(instruction.Mnemonic, - instructionArgs, - instruction.Reference, - instruction.Set, - instruction.Opcode) - end +---- Extension / Mode Switching Support +E2Helper.Modes = {} +E2Helper.CurrentMode = "E2" -- Key for accessing mode. + +function E2Helper:RegisterMode(name) + if self.Modes[name] then + -- Don't overwrite a previously existing mode if possible + -- If an addon really wants to do so, they have access to the E2Helper mode table. + return false + else + -- Name is available, return a table to be set up by caller. + local ModeTable = { + Descriptions = {}, -- Item descriptions + Items = {}, -- Items + -- There should be a ModeSetup function here taking the E2Helper table as an argument. + -- Optionally, as well, a ModeSwitch function, taking the E2Helper as an argument. + -- Will be called on switch, before the new mode's ModeSetup, used for teardown if necessary. + } + self.Modes[name] = ModeTable + return ModeTable end end -- Which tables are we going to use? local function CurrentDescs() - if E2Helper.CurrentMode == true then - return E2Helper.Descriptions - else - return E2Helper.CPUDescriptions - end + return E2Helper.Modes[E2Helper.CurrentMode].Descriptions end local function CurrentTable() - if E2Helper.CurrentMode == true then - return wire_expression2_funcs - else - return E2Helper.CPUTable + return E2Helper.Modes[E2Helper.CurrentMode].Items +end + +function E2Helper:SetMode(key) + local mode = self.Modes[key or false] + local curMode = self.Modes[self.CurrentMode] + if mode then + if curMode.ModeSwitch then + curMode.ModeSwitch(self) -- For teardown of previous setup if needed. + end + self.CurrentMode = key + if mode.ModeSetup then + mode.ModeSetup(self) + end + self.Update() + return true end + return false -- No mode. end ------------------------------- @@ -105,6 +108,37 @@ local function getdesc(name, args) return CurrentDescs()[string.format("%s(%s)", name, args)] or CurrentDescs()[name] end +-- Register the E2 mode, this shouldn't need be done twice because it indexes global for its info +local E2Mode = E2Helper:RegisterMode("E2") +if E2Mode then + local E2ModeMetatable = { + __index = function(self,key) + if key == "Items" then return wire_expression2_funcs end + if key == "Descriptions" then return E2Helper.Descriptions end + return nil + end + } + E2Mode.Items = nil + E2Mode.Descriptions = nil + -- The metatable is needed because storing a ref to wire_expression2_funcs + -- and then causing e2 to reload (like changing extensions) doesn't update the ref + -- or something like that, it causes e2helper to access nil values. + setmetatable(E2Mode,E2ModeMetatable) + E2Mode.ModeSetup = function(E2HelperPanel) + E2HelperPanel.FunctionColumn:SetName("Function") + E2HelperPanel.FunctionColumn:SetWidth(126) + E2HelperPanel.FromColumn:SetName("From") + E2HelperPanel.FromColumn:SetWidth(80) + E2HelperPanel.TakesColumn:SetName("Takes") + E2HelperPanel.TakesColumn:SetWidth(60) + E2HelperPanel.ReturnsColumn:SetName("Returns") + E2HelperPanel.ReturnsColumn:SetWidth(60) + E2HelperPanel.CostColumn:SetName("Cost") + E2HelperPanel.CostColumn:SetWidth(40) + end + +end + function E2Helper.Create(reset) E2Helper.Frame = vgui.Create("DFrame") @@ -139,14 +173,25 @@ function E2Helper.Create(reset) E2Helper.ResultFrame:SetPos(5, 60) E2Helper.ResultFrame:SetSize(330, 240) E2Helper.ResultFrame:SetMultiSelect(false) - E2Helper.ResultFrame:AddColumn("Function"):SetWidth(126) - E2Helper.FromColumn = E2Helper.ResultFrame:AddColumn("From") - E2Helper.FromColumn:SetWidth(80) - E2Helper.ResultFrame:AddColumn("Takes"):SetWidth(60) - E2Helper.ReturnsColumn = E2Helper.ResultFrame:AddColumn("Returns") - E2Helper.ReturnsColumn:SetWidth(60) - E2Helper.CostColumn = E2Helper.ResultFrame:AddColumn("Cost") - E2Helper.CostColumn:SetWidth(40) + -- Default 5 columns, accessable by index here for more modularity. + E2Helper.Columns = { + E2Helper.ResultFrame:AddColumn("Function"), + E2Helper.ResultFrame:AddColumn("From"), + E2Helper.ResultFrame:AddColumn("Takes"), + E2Helper.ResultFrame:AddColumn("Returns"), + E2Helper.ResultFrame:AddColumn("Cost"), + } + E2Helper.Columns[1]:SetWidth(126) + E2Helper.Columns[2]:SetWidth(80) + E2Helper.Columns[3]:SetWidth(60) + E2Helper.Columns[4]:SetWidth(60) + E2Helper.Columns[5]:SetWidth(40) + -- Name keys for backwards compatibility + E2Helper.FunctionColumn = E2Helper.Columns[1] + E2Helper.FromColumn = E2Helper.Columns[2] + E2Helper.TakesColumn = E2Helper.Columns[3] + E2Helper.ReturnsColumn = E2Helper.Columns[4] + E2Helper.CostColumn = E2Helper.Columns[5] function E2Helper.ResultFrame:OnClickLine(line) self:ClearSelection() @@ -239,32 +284,21 @@ function E2Helper.Create(reset) E2Helper.MaxLabel:SetText("Max results:") E2Helper.MaxLabel:SizeToContents() - E2Helper.E2Mode = vgui.Create("DCheckBoxLabel", E2Helper.Frame) - E2Helper.E2Mode:SetPos(90, 384) - E2Helper.E2Mode:SetText("E2") - E2Helper.E2Mode:SetValue(true) - E2Helper.E2Mode:SizeToContents() - function E2Helper.E2Mode.Button:Toggle() - self:SetValue(true) - E2Helper.CurrentMode = true - E2Helper.CPUMode:SetValue(false) - E2Helper.CostColumn:SetName("Cost") - E2Helper.ReturnsColumn:SetName("Returns") - E2Helper.Update() + E2Helper.ModeSelect = vgui.Create("DComboBox", E2Helper.Frame) + E2Helper.ModeSelect:SetPos(90, 384) + local modecount = 0 + for k,_ in pairs(E2Helper.Modes) do + modecount = modecount + 1 + E2Helper.ModeSelect:AddChoice(k) end - - E2Helper.CPUMode = vgui.Create("DCheckBoxLabel", E2Helper.Frame) - E2Helper.CPUMode:SetPos(90, 404) - E2Helper.CPUMode:SetText("CPU/GPU") - E2Helper.CPUMode:SetValue(false) - E2Helper.CPUMode:SizeToContents() - function E2Helper.CPUMode.Button:Toggle() - self:SetValue(true) - E2Helper.CurrentMode = false - E2Helper.E2Mode:SetValue(false) - E2Helper.CostColumn:SetName("Opcode") - E2Helper.ReturnsColumn:SetName("Platform") - E2Helper.Update() + if modecount < 2 then + -- If we don't have enough modes it's pointless to display this I think. + E2Helper.ModeSelect:Hide() + else + E2Helper.ModeSelect:Show() + end + function E2Helper.ModeSelect:OnSelect(ind,value,data) + E2Helper:SetMode(value) end E2Helper.NameEntry.OnTextChanged = delayed(0.1, E2Helper.Update) @@ -293,7 +327,7 @@ function E2Helper.Create(reset) end function E2Helper.GetFunctionSyntax(func, args, rets) - if E2Helper.CurrentMode == true then + if E2Helper.CurrentMode == "E2" then local signature = func .. "(" .. args .. ")" local ret = E2Lib.generate_signature(signature, rets, wire_expression2_funcs[signature].argnames) if rets ~= "" then ret = ret:sub(1, 1):upper() .. ret:sub(2) end @@ -309,6 +343,7 @@ function E2Helper.Update() cookie_update() E2Helper.ResultFrame:Clear() + E2Helper.ModeSelect:SetValue(E2Helper.CurrentMode) local search_name, search_from, search_args, search_rets = E2Helper.NameEntry:GetValue():lower(), E2Helper.FromEntry:GetValue():lower(), E2Helper.ParamEntry:GetValue():lower(), E2Helper.ReturnEntry:GetValue():lower() local count = 0 @@ -317,7 +352,7 @@ function E2Helper.Update() -- add E2 constants E2Helper.constants = {} - if E2Helper.CurrentMode == true then + if E2Helper.CurrentMode == "E2" then for k, v in pairs(wire_expression2_constants) do -- constants have no arguments and no cost local name, args, rets, cost = k, nil, v.type, 0 @@ -332,7 +367,7 @@ function E2Helper.Update() if count < maxcount then for _, v in pairs(CurrentTable()) do - if E2Helper.CurrentMode == true then + if E2Helper.CurrentMode == "E2" then local from, signature, rets, cost = v.extension, v[1], v[2], v[4] local name, args = string.match(signature, "^([^(]+)%(([^)]*)%)$") @@ -347,11 +382,12 @@ function E2Helper.Update() if count >= maxcount then break end end else - local funcname, args, forwhat, functype = unpack(v) - if (funcname:lower():find(search_name, 1, true) and + local funcname, extension, args, forwhat, functype = unpack(v) + if funcname:lower():find(search_name, 1, true) and + extension:lower():find(search_from, 1, true) and args:lower():find(search_args, 1, true) and - forwhat:lower():find(search_rets, 1, true)) then - local line = E2Helper.ResultFrame:AddLine(funcname, "", args, forwhat, functype) -- TODO: make this column useful for CPU/GPU + forwhat:lower():find(search_rets, 1, true) then + local line = E2Helper.ResultFrame:AddLine(funcname, extension, args, forwhat, functype) if tooltip then line:SetTooltip(funcname .. " " .. args) end count = count + 1 if count >= maxcount then break end @@ -379,23 +415,6 @@ function E2Helper.Show(searchtext) end end -function E2Helper.UseE2(nEditorType) - E2Helper.CurrentMode = false - E2Helper.E2Mode:Toggle() - local val = E2Helper.ReturnEntry:GetValue() - if val and (val == "CPU" or val == "GPU") then E2Helper.ReturnEntry:SetText("") end - E2Helper.CostColumn:SetName("Cost") - E2Helper.ReturnsColumn:SetName("Returns") -end - -function E2Helper.UseCPU(nEditorType) - E2Helper.CurrentMode = true - E2Helper.CPUMode:Toggle() - E2Helper.CostColumn:SetName("Type") - E2Helper.ReturnsColumn:SetName("For What") - E2Helper.ReturnEntry:SetText(nEditorType) -end - local delayed_cookie_update = delayed(1, cookie_update) local lastw, lasth @@ -417,8 +436,7 @@ function E2Helper.Resize() E2Helper.DescriptionEntry:SetPos(orig.DescriptionEntry[1], orig.DescriptionEntry[2] + changeh) E2Helper.DescriptionEntry:SetSize(orig.DescriptionEntry[3] + changew, orig.DescriptionEntry[4]) E2Helper.ResultFrame:SetSize(orig.ResultFrame[3] + changew, orig.ResultFrame[4] + changeh) - E2Helper.E2Mode:SetPos(orig.E2Mode[1], orig.E2Mode[2] + changeh) - E2Helper.CPUMode:SetPos(orig.CPUMode[1], orig.CPUMode[2] + changeh) + E2Helper.ModeSelect:SetPos(orig.ModeSelect[1] + changew, orig.ModeSelect[2] + changeh) E2Helper.NameEntry:SetSize(orig.NameEntry[3] + changew * 0.25, orig.NameEntry[4]) E2Helper.FromEntry:SetPos(orig.FromEntry[1] + changew * 0.25, orig.FromEntry[2]) diff --git a/lua/wire/client/text_editor/modes/e2.lua b/lua/wire/client/text_editor/modes/e2.lua index 5bae311ed9..b5c7cf6aac 100644 --- a/lua/wire/client/text_editor/modes/e2.lua +++ b/lua/wire/client/text_editor/modes/e2.lua @@ -3,7 +3,13 @@ local string_sub = string.sub local string_gmatch = string.gmatch local string_gsub = string.gsub -local EDITOR = {} +local EDITOR = { + UseValidator = true, + Validator = function(editor,source,file) + return E2Lib.Validate(source) + end, + UseSoundBrowser = true, +} local function istype(tp) return wire_expression_types[tp:upper()] or tp == "number" diff --git a/lua/wire/client/text_editor/wire_expression2_editor.lua b/lua/wire/client/text_editor/wire_expression2_editor.lua index a2de0a26cf..42784a886d 100644 --- a/lua/wire/client/text_editor/wire_expression2_editor.lua +++ b/lua/wire/client/text_editor/wire_expression2_editor.lua @@ -1703,7 +1703,7 @@ function Editor:Validate(gotoerror) local problems_errors, problems_warnings = {}, {} if self.EditorType == "E2" then - local errors, _, warnings, compiler = E2Lib.Validate(self:GetCode()) + local errors, _, warnings, compiler = self:Validator(self:GetCode(), self:GetChosenFile()) if not errors then ---@cast compiler -? self:SetValidateData(compiler) @@ -1744,10 +1744,10 @@ function Editor:Validate(gotoerror) problems_errors = errors end - elseif self.EditorType == "CPU" or self.EditorType == "GPU" or self.EditorType == "SPU" then + elseif self.Validator then header_color = Color(64, 64, 64, 180) header_text = "Recompiling..." - CPULib.Validate(self, self:GetCode(), self:GetChosenFile()) + self:Validator(self:GetCode(), self:GetChosenFile()) end self.C.Val:Update(problems_errors, problems_warnings, header_text, header_color) @@ -2024,25 +2024,10 @@ function Editor:Setup(nTitle, nLocation, nEditorType) self.EditorType = nEditorType self.C.Browser:Setup(nLocation) - local textEditorModes = { - CPU = "ZCPU", - GPU = "ZCPU", - SPU = "ZCPU", - E2 = "E2", - [""] = "Default" - } - - local helpModes = { - CPU = E2Helper.UseCPU, - GPU = E2Helper.UseCPU, - SPU = E2Helper.UseCPU, - E2 = E2Helper.UseE2 - } - - self:SetEditorMode(textEditorModes[nEditorType or ""]) - + self:SetEditorMode(nEditorType or "Default") + local editorMode = WireTextEditor.Modes[self:GetEditorMode() or "Default"] - local helpMode = helpModes[nEditorType or ""] + local helpMode = E2Helper.Modes[nEditorType or ""] or E2Helper.Modes[(editorMode and editorMode.E2HelperCategory) or ""] if helpMode then -- Add "E2Helper" button local E2Help = vgui.Create("Button", self.C.Menu) E2Help:SetSize(58, 20) @@ -2050,15 +2035,23 @@ function Editor:Setup(nTitle, nLocation, nEditorType) E2Help:SetText("E2Helper") E2Help.DoClick = function() E2Helper.Show() - helpMode(nEditorType) - E2Helper.Update() + if editorMode and editorMode.E2HelperCategory then + E2Helper:SetMode(editorMode.E2HelperCategory) + else + E2Helper:SetMode(nEditorType) + end end self.C.E2Help = E2Help end - - local useValidator = nEditorType ~= nil - local useSoundBrowser = nEditorType == "SPU" or nEditorType == "E2" - local useDebugger = nEditorType == "CPU" + local useValidator = false + local useSoundBrowser = false + if editorMode then + useValidator = editorMode.UseValidator + useSoundBrowser = editorMode.UseSoundBrowser + if useValidator and editorMode.Validator then + self.Validator = editorMode.Validator -- Takes self, self:GetCode(), self:GetChosenFile() + end + end if not useValidator then self.C.Val:SetVisible(false) @@ -2073,45 +2066,6 @@ function Editor:Setup(nTitle, nLocation, nEditorType) self.C.SoundBrw = SoundBrw end - if useDebugger then - -- Add "step forward" button - local DebugForward = self:addComponent(vgui.Create("Button", self), -306, 31, -226, 20) - DebugForward:SetText("Step Forward") - DebugForward.Font = "E2SmallFont" - DebugForward.DoClick = function() - local currentPosition = CPULib.Debugger.PositionByPointer[CPULib.Debugger.Variables.IP] - if currentPosition then - local linePointers = CPULib.Debugger.PointersByLine[currentPosition.Line .. ":" .. currentPosition.File] - if linePointers then -- Run till end of line - RunConsoleCommand("wire_cpulib_debugstep", linePointers[2]) - else -- Run just once - RunConsoleCommand("wire_cpulib_debugstep") - end - else -- Run just once - RunConsoleCommand("wire_cpulib_debugstep") - end - -- Reset interrupt text - CPULib.InterruptText = nil - end - self.C.DebugForward = DebugForward - - -- Add "reset" button - local DebugReset = self:addComponent(vgui.Create("Button", self), -346, 31, -306, 20) - DebugReset:SetText("Reset") - DebugReset.DoClick = function() - RunConsoleCommand("wire_cpulib_debugreset") - -- Reset interrupt text - CPULib.InterruptText = nil - end - self.C.DebugReset = DebugReset - - -- Add "run" button - local DebugRun = self:addComponent(vgui.Create("Button", self), -381, 31, -346, 20) - DebugRun:SetText("Run") - DebugRun.DoClick = function() RunConsoleCommand("wire_cpulib_debugrun") end - self.C.DebugRun = DebugRun - end - if nEditorType == "E2" then self.E2 = true end