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