6d5df66 - Update to current
[wowui.git] / libs / AceConsole-3.0 / AceConsole-3.0.lua
1 --- **AceConsole-3.0** provides registration facilities for slash commands.
2 -- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
3 -- to your addons individual needs.
4 --
5 -- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by 
6 -- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
7 -- and can be accessed directly, without having to explicitly call AceConsole itself.\\
8 -- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
9 -- make into AceConsole.
10 -- @class file
11 -- @name AceConsole-3.0
12 -- @release $Id: AceConsole-3.0.lua 878 2009-11-02 18:51:58Z nevcairiel $
13 local MAJOR,MINOR = "AceConsole-3.0", 7
14
15 local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
16
17 if not AceConsole then return end -- No upgrade needed
18
19 AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in.
20 AceConsole.commands = AceConsole.commands or {} -- table containing commands registered
21 AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable
22
23 -- Lua APIs
24 local tconcat, tostring, select = table.concat, tostring, select
25 local type, pairs, error = type, pairs, error
26 local format, strfind, strsub = string.format, string.find, string.sub
27 local max = math.max
28
29 -- WoW APIs
30 local _G = _G
31
32 -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
33 -- List them here for Mikk's FindGlobals script
34 -- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
35
36 local tmp={}
37 local function Print(self,frame,...)
38         local n=0
39         if self ~= AceConsole then
40                 n=n+1
41                 tmp[n] = "|cff33ff99"..tostring( self ).."|r:"
42         end
43         for i=1, select("#", ...) do
44                 n=n+1
45                 tmp[n] = tostring(select(i, ...))
46         end
47         frame:AddMessage( tconcat(tmp," ",1,n) )
48 end
49
50 --- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
51 -- @paramsig [chatframe ,] ...
52 -- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
53 -- @param ... List of any values to be printed
54 function AceConsole:Print(...)
55         local frame = ...
56         if type(frame) == "table" and frame.AddMessage then     -- Is first argument something with an .AddMessage member?
57                 return Print(self, frame, select(2,...))
58         else
59                 return Print(self, DEFAULT_CHAT_FRAME, ...)
60         end
61 end
62
63
64 --- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
65 -- @paramsig [chatframe ,] "format"[, ...]
66 -- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
67 -- @param format Format string - same syntax as standard Lua format()
68 -- @param ... Arguments to the format string
69 function AceConsole:Printf(...)
70         local frame = ...
71         if type(frame) == "table" and frame.AddMessage then     -- Is first argument something with an .AddMessage member?
72                 return Print(self, frame, format(select(2,...)))
73         else
74                 return Print(self, DEFAULT_CHAT_FRAME, format(...))
75         end
76 end
77
78
79
80
81 --- Register a simple chat command
82 -- @param command Chat command to be registered WITHOUT leading "/"
83 -- @param func Function to call when the slash command is being used (funcref or methodname)
84 -- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
85 function AceConsole:RegisterChatCommand( command, func, persist )
86         if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand( "command", func[, persist ]): 'command' - expected a string]], 2) end
87         
88         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
89         
90         local name = "ACECONSOLE_"..command:upper()
91         
92         if type( func ) == "string" then
93                 SlashCmdList[name] = function(input, editBox)
94                         self[func](self, input, editBox)
95                 end
96         else
97                 SlashCmdList[name] = func
98         end
99         _G["SLASH_"..name.."1"] = "/"..command:lower()
100         AceConsole.commands[command] = name
101         -- non-persisting commands are registered for enabling disabling
102         if not persist then
103                 if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end
104                 AceConsole.weakcommands[self][command] = func
105         end
106         return true
107 end
108
109 --- Unregister a chatcommand
110 -- @param command Chat command to be unregistered WITHOUT leading "/"
111 function AceConsole:UnregisterChatCommand( command )
112         local name = AceConsole.commands[command]
113         if name then
114                 SlashCmdList[name] = nil
115                 _G["SLASH_" .. name .. "1"] = nil
116                 hash_SlashCmdList["/" .. command:upper()] = nil
117                 AceConsole.commands[command] = nil
118         end
119 end
120
121 --- Get an iterator over all Chat Commands registered with AceConsole
122 -- @return Iterator (pairs) over all commands
123 function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end
124
125
126 local function nils(n, ...)
127         if n>1 then
128                 return nil, nils(n-1, ...)
129         elseif n==1 then
130                 return nil, ...
131         else
132                 return ...
133         end
134 end
135         
136
137 --- Retreive one or more space-separated arguments from a string. 
138 -- Treats quoted strings and itemlinks as non-spaced.
139 -- @param string The raw argument string
140 -- @param numargs How many arguments to get (default 1)
141 -- @param startpos Where in the string to start scanning (default  1)
142 -- @return Returns arg1, arg2, ..., nextposition\\
143 -- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string.
144 function AceConsole:GetArgs(str, numargs, startpos)
145         numargs = numargs or 1
146         startpos = max(startpos or 1, 1)
147         
148         local pos=startpos
149
150         -- find start of new arg
151         pos = strfind(str, "[^ ]", pos)
152         if not pos then -- whoops, end of string
153                 return nils(numargs, 1e9)
154         end
155
156         if numargs<1 then
157                 return pos
158         end
159
160         -- quoted or space separated? find out which pattern to use
161         local delim_or_pipe
162         local ch = strsub(str, pos, pos)
163         if ch=='"' then
164                 pos = pos + 1
165                 delim_or_pipe='([|"])'
166         elseif ch=="'" then
167                 pos = pos + 1
168                 delim_or_pipe="([|'])"
169         else
170                 delim_or_pipe="([| ])"
171         end
172         
173         startpos = pos
174         
175         while true do
176                 -- find delimiter or hyperlink
177                 local ch,_
178                 pos,_,ch = strfind(str, delim_or_pipe, pos)
179                 
180                 if not pos then break end
181                 
182                 if ch=="|" then
183                         -- some kind of escape
184                         
185                         if strsub(str,pos,pos+1)=="|H" then
186                                 -- It's a |H....|hhyper link!|h
187                                 pos=strfind(str, "|h", pos+2)   -- first |h
188                                 if not pos then break end
189                                 
190                                 pos=strfind(str, "|h", pos+2)   -- second |h
191                                 if not pos then break end
192                         elseif strsub(str,pos, pos+1) == "|T" then
193                                 -- It's a |T....|t  texture
194                                 pos=strfind(str, "|t", pos+2)
195                                 if not pos then break end
196                         end
197                         
198                         pos=pos+2 -- skip past this escape (last |h if it was a hyperlink)
199                 
200                 else
201                         -- found delimiter, done with this arg
202                         return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1)
203                 end
204                 
205         end
206         
207         -- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink)
208         return strsub(str, startpos), nils(numargs-1, 1e9)
209 end
210
211
212 --- embedding and embed handling
213
214 local mixins = {
215         "Print",
216         "Printf",
217         "RegisterChatCommand", 
218         "UnregisterChatCommand",
219         "GetArgs",
220
221
222 -- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
223 -- @param target target object to embed AceBucket in
224 function AceConsole:Embed( target )
225         for k, v in pairs( mixins ) do
226                 target[v] = self[v]
227         end
228         self.embeds[target] = true
229         return target
230 end
231
232 function AceConsole:OnEmbedEnable( target )
233         if AceConsole.weakcommands[target] then
234                 for command, func in pairs( AceConsole.weakcommands[target] ) do
235                         target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry
236                 end
237         end
238 end
239
240 function AceConsole:OnEmbedDisable( target )
241         if AceConsole.weakcommands[target] then
242                 for command, func in pairs( AceConsole.weakcommands[target] ) do
243                         target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care?
244                 end
245         end
246 end
247
248 for addon in pairs(AceConsole.embeds) do
249         AceConsole:Embed(addon)
250 end