--- **AceConsole-3.0** provides registration facilities for slash commands. |
-- You can register slash commands to your custom functions and use the `GetArgs` function to parse them |
-- to your addons individual needs. |
-- |
-- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by |
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object |
-- and can be accessed directly, without having to explicitly call AceConsole itself.\\ |
-- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you |
-- make into AceConsole. |
-- @class file |
-- @name AceConsole-3.0 |
-- @release $Id: AceConsole-3.0.lua 878 2009-11-02 18:51:58Z nevcairiel $ |
local MAJOR,MINOR = "AceConsole-3.0", 7 |
|
local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR) |
|
if not AceConsole then return end -- No upgrade needed |
|
AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in. |
AceConsole.commands = AceConsole.commands or {} -- table containing commands registered |
AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable |
|
-- Lua APIs |
local tconcat, tostring, select = table.concat, tostring, select |
local type, pairs, error = type, pairs, error |
local format, strfind, strsub = string.format, string.find, string.sub |
local max = math.max |
|
-- WoW APIs |
local _G = _G |
|
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded |
-- List them here for Mikk's FindGlobals script |
-- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList |
|
local tmp={} |
local function Print(self,frame,...) |
local n=0 |
if self ~= AceConsole then |
n=n+1 |
tmp[n] = "|cff33ff99"..tostring( self ).."|r:" |
end |
for i=1, select("#", ...) do |
n=n+1 |
tmp[n] = tostring(select(i, ...)) |
end |
frame:AddMessage( tconcat(tmp," ",1,n) ) |
end |
|
--- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function) |
-- @paramsig [chatframe ,] ... |
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function) |
-- @param ... List of any values to be printed |
function AceConsole:Print(...) |
local frame = ... |
if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member? |
return Print(self, frame, select(2,...)) |
else |
return Print(self, DEFAULT_CHAT_FRAME, ...) |
end |
end |
|
|
--- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function) |
-- @paramsig [chatframe ,] "format"[, ...] |
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function) |
-- @param format Format string - same syntax as standard Lua format() |
-- @param ... Arguments to the format string |
function AceConsole:Printf(...) |
local frame = ... |
if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member? |
return Print(self, frame, format(select(2,...))) |
else |
return Print(self, DEFAULT_CHAT_FRAME, format(...)) |
end |
end |
|
|
|
|
--- Register a simple chat command |
-- @param command Chat command to be registered WITHOUT leading "/" |
-- @param func Function to call when the slash command is being used (funcref or methodname) |
-- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true) |
function AceConsole:RegisterChatCommand( command, func, persist ) |
if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end |
|
if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk |
|
local name = "ACECONSOLE_"..command:upper() |
|
if type( func ) == "string" then |
SlashCmdList[name] = function(input, editBox) |
self[func](self, input, editBox) |
end |
else |
SlashCmdList[name] = func |
end |
_G["SLASH_"..name.."1"] = "/"..command:lower() |
AceConsole.commands[command] = name |
-- non-persisting commands are registered for enabling disabling |
if not persist then |
if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end |
AceConsole.weakcommands[self][command] = func |
end |
return true |
end |
|
--- Unregister a chatcommand |
-- @param command Chat command to be unregistered WITHOUT leading "/" |
function AceConsole:UnregisterChatCommand( command ) |
local name = AceConsole.commands[command] |
if name then |
SlashCmdList[name] = nil |
_G["SLASH_" .. name .. "1"] = nil |
hash_SlashCmdList["/" .. command:upper()] = nil |
AceConsole.commands[command] = nil |
end |
end |
|
--- Get an iterator over all Chat Commands registered with AceConsole |
-- @return Iterator (pairs) over all commands |
function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end |
|
|
local function nils(n, ...) |
if n>1 then |
return nil, nils(n-1, ...) |
elseif n==1 then |
return nil, ... |
else |
return ... |
end |
end |
|
|
--- Retreive one or more space-separated arguments from a string. |
-- Treats quoted strings and itemlinks as non-spaced. |
-- @param string The raw argument string |
-- @param numargs How many arguments to get (default 1) |
-- @param startpos Where in the string to start scanning (default 1) |
-- @return Returns arg1, arg2, ..., nextposition\\ |
-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string. |
function AceConsole:GetArgs(str, numargs, startpos) |
numargs = numargs or 1 |
startpos = max(startpos or 1, 1) |
|
local pos=startpos |
|
-- find start of new arg |
pos = strfind(str, "[^ ]", pos) |
if not pos then -- whoops, end of string |
return nils(numargs, 1e9) |
end |
|
if numargs<1 then |
return pos |
end |
|
-- quoted or space separated? find out which pattern to use |
local delim_or_pipe |
local ch = strsub(str, pos, pos) |
if ch=='"' then |
pos = pos + 1 |
delim_or_pipe='([|"])' |
elseif ch=="'" then |
pos = pos + 1 |
delim_or_pipe="([|'])" |
else |
delim_or_pipe="([| ])" |
end |
|
startpos = pos |
|
while true do |
-- find delimiter or hyperlink |
local ch,_ |
pos,_,ch = strfind(str, delim_or_pipe, pos) |
|
if not pos then break end |
|
if ch=="|" then |
-- some kind of escape |
|
if strsub(str,pos,pos+1)=="|H" then |
-- It's a |H....|hhyper link!|h |
pos=strfind(str, "|h", pos+2) -- first |h |
if not pos then break end |
|
pos=strfind(str, "|h", pos+2) -- second |h |
if not pos then break end |
elseif strsub(str,pos, pos+1) == "|T" then |
-- It's a |T....|t texture |
pos=strfind(str, "|t", pos+2) |
if not pos then break end |
end |
|
pos=pos+2 -- skip past this escape (last |h if it was a hyperlink) |
|
else |
-- found delimiter, done with this arg |
return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1) |
end |
|
end |
|
-- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink) |
return strsub(str, startpos), nils(numargs-1, 1e9) |
end |
|
|
--- embedding and embed handling |
|
local mixins = { |
"Print", |
"Printf", |
"RegisterChatCommand", |
"UnregisterChatCommand", |
"GetArgs", |
} |
|
-- Embeds AceConsole into the target object making the functions from the mixins list available on target:.. |
-- @param target target object to embed AceBucket in |
function AceConsole:Embed( target ) |
for k, v in pairs( mixins ) do |
target[v] = self[v] |
end |
self.embeds[target] = true |
return target |
end |
|
function AceConsole:OnEmbedEnable( target ) |
if AceConsole.weakcommands[target] then |
for command, func in pairs( AceConsole.weakcommands[target] ) do |
target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry |
end |
end |
end |
|
function AceConsole:OnEmbedDisable( target ) |
if AceConsole.weakcommands[target] then |
for command, func in pairs( AceConsole.weakcommands[target] ) do |
target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care? |
end |
end |
end |
|
for addon in pairs(AceConsole.embeds) do |
AceConsole:Embed(addon) |
end |