Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,3 @@ jobs:
files: |
dist/DevForge-${{ steps.ver.outputs.tag }}.zip
generate_release_notes: true

- name: Package and publish
uses: BigWigsMods/packager@v2
with:
pandoc: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CF_API_KEY: ${{ secrets.CF_API_KEY }}
27 changes: 25 additions & 2 deletions DevForge/Core/Init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,44 @@ DF.frame = CreateFrame("Frame")
DF.frame:RegisterEvent("ADDON_LOADED")
DF.frame:RegisterEvent("PLAYER_LOGOUT")

-- After /reload, named frames survive but all Lua scripts are wiped.
-- Hide the stale window immediately so the user can't interact with a
-- scriptless zombie. We record whether it was showing so we can reopen
-- after the fresh UI is built.
local staleWasShown = false
do
local stale = _G["DevForgeMainWindow"]
if stale then
staleWasShown = stale:IsShown()
stale:Hide()
stale:EnableMouse(false)
end
end

DF.frame:SetScript("OnEvent", function(_, event, ...)
if event == "ADDON_LOADED" then
local name = ...
if name == ADDON_NAME then
DF.frame:UnregisterEvent("ADDON_LOADED")
if DF.Schema then
DF.Schema:Init()
local ok, err = pcall(DF.Schema.Init, DF.Schema)
if not ok then print("|cFFFF4444DevForge: Schema:Init() error:|r " .. tostring(err)) end
end
-- Install error handler hooks immediately so no errors are missed
if DF.ErrorHandler then
DF.ErrorHandler:Init()
local ok, err = pcall(DF.ErrorHandler.Init, DF.ErrorHandler)
if not ok then print("|cFFFF4444DevForge: ErrorHandler:Init() error:|r " .. tostring(err)) end
end
if DF.EventBus then
DF.EventBus:Fire("DF_ADDON_LOADED")
end

-- If the window was open before /reload, reopen it seamlessly
if staleWasShown then
C_Timer.After(0, function()
DF:Toggle()
end)
end
end
elseif event == "PLAYER_LOGOUT" then
if DF.EventBus then
Expand Down
2 changes: 1 addition & 1 deletion DevForge/DevForge.toc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Notes: In-game dev toolkit: inspector, editor, console, events, errors, APIs, textures, performance
## Author: hatdragon
## Contributors:
## Version: r1.0.1
## Version: r1.0.10

## SavedVariables: DevForgeDB
## IconTexture: Interface\Icons\INV_Gizmo_02
Expand Down
63 changes: 50 additions & 13 deletions DevForge/Modules/ErrorHandler/ErrorHandler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,25 @@ local function OnBugGrabbed(_, bugObj)
)
end

-- Correct stack data extraction (mirrors BugGrabber's GetErrorData approach)
local function GetErrorData()
local currentStackHeight = GetCallstackHeight and GetCallstackHeight()
local errorCallStackHeight = GetErrorCallstackHeight and GetErrorCallstackHeight()
if currentStackHeight and errorCallStackHeight then
local debugStackLevel = currentStackHeight - (errorCallStackHeight - 1)
return debugstack(debugStackLevel), debuglocals(debugStackLevel)
end
return debugstack(3), debuglocals(3)
end

-- Self-hook error handler (used when BugGrabber is absent)
local inHandler = false
local function OnLuaError(message)
local stack = debugstack(3)
local locals = debuglocals(3)
if inHandler then return end
inHandler = true
local stack, locals = GetErrorData()
ProcessError(message, stack, locals, "error")
inHandler = false
end

function Handler:Init()
Expand Down Expand Up @@ -147,27 +161,50 @@ function Handler:Init()
BugGrabber.RegisterAddonActionCallback(OnBugGrabbed)
end
else
-- Self-hook using Blizzard 12.x API
-- Self-hook: try each approach individually with pcall so one
-- failure doesn't block the rest. Blizzard_ScriptErrors may
-- assert when addons interact with the error-handler chain.
local hooked = false
if _G.AddLuaErrorHandler then
AddLuaErrorHandler(OnLuaError)
elseif _G.seterrorhandler then
-- Fallback: wrap existing error handler
local ok = pcall(AddLuaErrorHandler, OnLuaError)
hooked = ok
end
if not hooked and _G.seterrorhandler then
local oldHandler = geterrorhandler()
seterrorhandler(function(msg)
local ok = pcall(seterrorhandler, function(msg)
OnLuaError(msg)
if oldHandler then
return oldHandler(msg)
end
end)
hooked = ok
end
end

-- Register for LUA_WARNING events
local warningFrame = CreateFrame("Frame")
warningFrame:RegisterEvent("LUA_WARNING")
warningFrame:SetScript("OnEvent", function(_, _, warnType, warnMessage)
ProcessError(warnMessage, nil, nil, "warning")
end)
-- Register for additional error-producing events
if not _G.BugGrabber then
local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("LUA_WARNING")
eventFrame:RegisterEvent("ADDON_ACTION_BLOCKED")
eventFrame:RegisterEvent("ADDON_ACTION_FORBIDDEN")

local badAddons = {}
eventFrame:SetScript("OnEvent", function(_, event, ...)
if event == "LUA_WARNING" then
local arg1, arg2 = ...
local warningText = arg2 or arg1
ProcessError(warningText, nil, nil, "warning")
elseif event == "ADDON_ACTION_BLOCKED" or event == "ADDON_ACTION_FORBIDDEN" then
local addonName, addonFunc = ...
local name = addonName or "<name>"
if not badAddons[name] then
badAddons[name] = true
local msg = ("[%s] AddOn '%s' tried to call the protected function '%s'."):format(event, name, addonFunc or "<func>")
ProcessError(msg, nil, nil, "error")
end
end
end)
end
end

function Handler:GetErrors()
Expand Down
1 change: 1 addition & 0 deletions DevForge/Modules/ErrorHandler/ErrorList.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function ErrList:Create(parent)
end

local row = CreateFrame("Button", nil, self.pane:GetContent())
row:RegisterForClicks("LeftButtonUp")
row:SetHeight(ROW_HEIGHT)

-- Selection highlight
Expand Down
22 changes: 1 addition & 21 deletions DevForge/Modules/ErrorHandler/ErrorMonitor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ DF.ModuleSystem:Register("ErrorHandler", function(sidebarParent, editorParent)
local pauseBtn = DF.Widgets:CreateButton(toolbar, "Pause", 60)
pauseBtn:SetPoint("LEFT", copyBtn, "RIGHT", 4, 0)

local toConsoleBtn = DF.Widgets:CreateButton(toolbar, "To Console", 80)
toConsoleBtn:SetPoint("LEFT", pauseBtn, "RIGHT", 8, 0)

-- Count label
local countLabel = toolbar:CreateFontString(nil, "OVERLAY")
countLabel:SetFontObject(DF.Theme:UIFont())
Expand Down Expand Up @@ -80,6 +77,7 @@ DF.ModuleSystem:Register("ErrorHandler", function(sidebarParent, editorParent)
errorList:Refresh()
errorDetail:Clear()
UpdateCount()
DF.EventBus:Fire("DF_ERRORS_CLEARED")
end)

-- Pause/Resume button
Expand All @@ -93,24 +91,6 @@ DF.ModuleSystem:Register("ErrorHandler", function(sidebarParent, editorParent)
end
end)

-- Copy to Console button
toConsoleBtn:SetScript("OnClick", function()
local text = errorDetail:GetText()
if text and text ~= "" then
-- Put error context into the REPL input
if DF.bottomPanel then
local input = DF.bottomPanel:GetInputLine()
if input then
-- Extract just the error message for the input
local errMsg = text:match("%[ERROR%] (.-)\n") or text:match("%[WARNING%] (.-)\n") or text:sub(1, 200)
input:SetText("-- Error: " .. errMsg)
input:Focus()
end
DF.bottomPanel:SelectTab("output")
end
end
end)

-- Live update callback via EventBus (set up by BottomPanel)
DF.EventBus:On("DF_ERROR_RECEIVED", function(err, isDuplicate)
errorList:Refresh()
Expand Down
78 changes: 73 additions & 5 deletions DevForge/Modules/EventMonitor/EventIndex.lua
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,24 @@ local CATEGORIES = {
},
}

-- Fast lookup set of all hardcoded event names
local knownEvents = {}
for _, cat in ipairs(CATEGORIES) do
for _, entry in ipairs(cat.events) do
knownEvents[entry.event] = true
end
end

-- Discovered events (captured at runtime, not in hardcoded index)
local discovered = {} -- event name -> true

-- Flat index for searching
local flatIndex = nil

local function InvalidateFlatIndex()
flatIndex = nil
end

local function BuildFlatIndex()
if flatIndex then return end
flatIndex = {}
Expand All @@ -439,6 +454,13 @@ local function BuildFlatIndex()
}
end
end
for event in pairs(discovered) do
flatIndex[#flatIndex + 1] = {
event = event,
desc = "Discovered at runtime",
category = "Discovered",
}
end
table.sort(flatIndex, function(a, b) return a.event < b.event end)
end

Expand Down Expand Up @@ -476,17 +498,63 @@ function Index:Search(query)
return results
end

-- Lookup a single event
-- Lookup a single event (fast path via hash set)
function Index:Lookup(eventName)
BuildFlatIndex()
for _, entry in ipairs(flatIndex) do
if entry.event == eventName then
return entry
if knownEvents[eventName] or discovered[eventName] then
BuildFlatIndex()
for _, entry in ipairs(flatIndex) do
if entry.event == eventName then
return entry
end
end
end
return nil
end

-- Check if an event is known (hardcoded or discovered)
function Index:IsKnown(eventName)
return knownEvents[eventName] or discovered[eventName] or false
end

-- Register a runtime-discovered event
function Index:RegisterDiscovered(eventName)
if knownEvents[eventName] or discovered[eventName] then return end
discovered[eventName] = true
InvalidateFlatIndex()
end

-- Persistence
function Index:LoadDiscovered()
if DevForgeDB and DevForgeDB.discoveredEvents then
for _, event in ipairs(DevForgeDB.discoveredEvents) do
if not knownEvents[event] then
discovered[event] = true
end
end
InvalidateFlatIndex()
end
end

function Index:SaveDiscovered()
if not DevForgeDB then return end
local list = {}
for event in pairs(discovered) do
list[#list + 1] = event
end
table.sort(list)
DevForgeDB.discoveredEvents = list
end

-- Get discovered events as a sorted list of { event, desc }
function Index:GetDiscovered()
local list = {}
for event in pairs(discovered) do
list[#list + 1] = { event = event, desc = "Discovered at runtime" }
end
table.sort(list, function(a, b) return a.event < b.event end)
return list
end

-- Build tree nodes for the TreeView widget
function Index:BuildTreeNodes(filterQuery)
local nodes = {}
Expand Down
Loading