flickerstreak@1
|
1 --[[
|
flickerstreak@1
|
2 Name: AceTab-2.0
|
flickerstreak@1
|
3 Revision: $Rev: 18708 $
|
flickerstreak@1
|
4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
|
flickerstreak@1
|
5 Website: http://www.wowace.com/
|
flickerstreak@1
|
6 Documentation: http://www..wowace.com/index.php/AceTab-2.0
|
flickerstreak@1
|
7 SVN: http://svn.wowace.com/root/trunk/Ace2/AceTab-2.0
|
flickerstreak@1
|
8 Description: A tab-completion library
|
flickerstreak@1
|
9 Dependencies: AceLibrary, AceEvent-2.0
|
flickerstreak@1
|
10 ]]
|
flickerstreak@1
|
11
|
flickerstreak@1
|
12 local MAJOR_VERSION = "AceTab-2.0"
|
flickerstreak@1
|
13 local MINOR_VERSION = "$Revision: 18708 $"
|
flickerstreak@1
|
14
|
flickerstreak@1
|
15 if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end
|
flickerstreak@1
|
16 if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
flickerstreak@1
|
17
|
flickerstreak@1
|
18 local AceEvent
|
flickerstreak@1
|
19 local AceTab = {}
|
flickerstreak@1
|
20 local _G = getfenv()
|
flickerstreak@1
|
21
|
flickerstreak@1
|
22 local hookedFrames = {}
|
flickerstreak@1
|
23 local framesHooked = {}
|
flickerstreak@1
|
24
|
flickerstreak@1
|
25 function AceTab:RegisterTabCompletion(descriptor, regex, wlfunc, usage, editframes)
|
flickerstreak@1
|
26 self:argCheck(descriptor, 2, "string")
|
flickerstreak@1
|
27 self:argCheck(regex, 3, "string", "table")
|
flickerstreak@1
|
28 self:argCheck(wlfunc, 4, "string", "function", "nil")
|
flickerstreak@1
|
29 self:argCheck(usage, 5, "string", "function", "boolean", "nil")
|
flickerstreak@1
|
30 self:argCheck(editframe, 6, "string", "table", "nil")
|
flickerstreak@1
|
31
|
flickerstreak@1
|
32 if type(regex) == "string" then regex = {regex} end
|
flickerstreak@1
|
33
|
flickerstreak@1
|
34 if type(wlfunc) == "string" and type(self[wlfunc]) ~= "function" then
|
flickerstreak@1
|
35 self:error("Cannot register function %q; it does not exist", wlfunc)
|
flickerstreak@1
|
36 end
|
flickerstreak@1
|
37
|
flickerstreak@1
|
38 if type(usage) == "string" and type(self[usage]) ~= "function" then
|
flickerstreak@1
|
39 self:error("Cannot register usage function %q; it does not exist", usage)
|
flickerstreak@1
|
40 end
|
flickerstreak@1
|
41
|
flickerstreak@1
|
42 if not editframes then editframes = {"ChatFrameEditBox"} end
|
flickerstreak@1
|
43
|
flickerstreak@1
|
44 if type(editframes) == "table" and editframes.Show then editframes = {editframes:GetName()} end
|
flickerstreak@1
|
45
|
flickerstreak@1
|
46 for _, frame in pairs(editframes) do
|
flickerstreak@1
|
47 local Gframe
|
flickerstreak@1
|
48 if type(frame) == "table" then
|
flickerstreak@1
|
49 Gframe = frame
|
flickerstreak@1
|
50 frame = frame:GetName()
|
flickerstreak@1
|
51 else
|
flickerstreak@1
|
52 Gframe = _G[frame]
|
flickerstreak@1
|
53 end
|
flickerstreak@1
|
54
|
flickerstreak@1
|
55 if type(Gframe) ~= "table" or not Gframe.Show then
|
flickerstreak@1
|
56 self:error("Cannot register frame %q; it does not exist", frame)
|
flickerstreak@1
|
57 frame = nil
|
flickerstreak@1
|
58 end
|
flickerstreak@1
|
59
|
flickerstreak@1
|
60 if frame then
|
flickerstreak@1
|
61 if Gframe:GetFrameType() ~= "EditBox" then
|
flickerstreak@1
|
62 self:error("Cannot register frame %q; it is not an EditBox", frame)
|
flickerstreak@1
|
63 frame = nil
|
flickerstreak@1
|
64 else
|
flickerstreak@1
|
65 if AceEvent and AceEvent:IsFullyInitialized() then
|
flickerstreak@1
|
66 if not framesHooked[Gframe] then
|
flickerstreak@1
|
67 framesHooked[Gframe] = true
|
flickerstreak@1
|
68 local orig = Gframe:GetScript("OnTabPressed")
|
flickerstreak@1
|
69 if type(orig) ~= "function" then
|
flickerstreak@1
|
70 orig = function() end
|
flickerstreak@1
|
71 end
|
flickerstreak@1
|
72 Gframe:SetScript("OnTabPressed", function()
|
flickerstreak@1
|
73 if self:OnTabPressed(Gframe) then
|
flickerstreak@1
|
74 return orig()
|
flickerstreak@1
|
75 end
|
flickerstreak@1
|
76 end)
|
flickerstreak@1
|
77 Gframe.curMatch = 0
|
flickerstreak@1
|
78 Gframe.matches = {}
|
flickerstreak@1
|
79 Gframe.pMatchLen = 0
|
flickerstreak@1
|
80 end
|
flickerstreak@1
|
81 else
|
flickerstreak@1
|
82 hookedFrames[frame] = true
|
flickerstreak@1
|
83 end
|
flickerstreak@1
|
84 end
|
flickerstreak@1
|
85 end
|
flickerstreak@1
|
86 end
|
flickerstreak@1
|
87
|
flickerstreak@1
|
88 if not self.registry[descriptor] then
|
flickerstreak@1
|
89 self.registry[descriptor] = {}
|
flickerstreak@1
|
90 end
|
flickerstreak@1
|
91
|
flickerstreak@1
|
92 if not self.registry[descriptor][self] then
|
flickerstreak@1
|
93 self.registry[descriptor][self] = {}
|
flickerstreak@1
|
94 end
|
flickerstreak@1
|
95 self.registry[descriptor][self] = {patterns = regex, wlfunc = wlfunc, usage = usage, frames = editframes}
|
flickerstreak@1
|
96
|
flickerstreak@1
|
97
|
flickerstreak@1
|
98 if not AceEvent and AceLibrary:HasInstance("AceEvent-2.0") then
|
flickerstreak@1
|
99 external(AceTab, "AceEvent-2.0", AceLibrary("AceEvent-2.0"))
|
flickerstreak@1
|
100 end
|
flickerstreak@1
|
101 if AceEvent then
|
flickerstreak@1
|
102 if not self:IsEventRegistered("AceEvent_FullyInitialized") then
|
flickerstreak@1
|
103 self:RegisterEvent("AceEvent_FullyInitialized", "AceEvent_FullyInitialized", true)
|
flickerstreak@1
|
104 end
|
flickerstreak@1
|
105 end
|
flickerstreak@1
|
106 end
|
flickerstreak@1
|
107
|
flickerstreak@1
|
108 function AceTab:IsTabCompletionRegistered(descriptor)
|
flickerstreak@1
|
109 self:argCheck(descriptor, 2, "string")
|
flickerstreak@1
|
110 return self.registry[descriptor] and self.registry[descriptor][self]
|
flickerstreak@1
|
111 end
|
flickerstreak@1
|
112
|
flickerstreak@1
|
113 function AceTab:UnregisterTabCompletion(descriptor)
|
flickerstreak@1
|
114 self:argCheck(descriptor, 2, "string")
|
flickerstreak@1
|
115 if self.registry[descriptor] and self.registry[descriptor][self] then
|
flickerstreak@1
|
116 self.registry[descriptor][self] = nil
|
flickerstreak@1
|
117 else
|
flickerstreak@1
|
118 self:error("Cannot unregister a tab completion (%s) that you have not registered.", descriptor)
|
flickerstreak@1
|
119 end
|
flickerstreak@1
|
120 end
|
flickerstreak@1
|
121
|
flickerstreak@1
|
122 local GCS
|
flickerstreak@1
|
123 GCS = function(s1, s2)
|
flickerstreak@1
|
124 if not s1 and not s2 then return end
|
flickerstreak@1
|
125 if not s1 then s1 = s2 end
|
flickerstreak@1
|
126 if not s2 then s2 = s1 end
|
flickerstreak@1
|
127 local s1len, s2len = string.len(s1), string.len(s2)
|
flickerstreak@1
|
128 if s2len < s1len then
|
flickerstreak@1
|
129 s1, s2 = s2, s1
|
flickerstreak@1
|
130 end
|
flickerstreak@1
|
131 if string.find(string.lower(s2), string.lower(s1)) then
|
flickerstreak@1
|
132 return s1
|
flickerstreak@1
|
133 else
|
flickerstreak@1
|
134 return GCS(string.sub(s1, 1, -2), s2)
|
flickerstreak@1
|
135 end
|
flickerstreak@1
|
136 end
|
flickerstreak@1
|
137 local pos
|
flickerstreak@1
|
138 local function CycleTab()
|
flickerstreak@1
|
139 this.pMatchLen = string.len(this.lMatch)
|
flickerstreak@1
|
140 local cMatch = 0
|
flickerstreak@1
|
141 local matched = false
|
flickerstreak@1
|
142 for desc, mList in pairs(this.matches) do
|
flickerstreak@1
|
143 if not matched then
|
flickerstreak@1
|
144 for _, m in ipairs(mList) do
|
flickerstreak@1
|
145 cMatch = cMatch + 1
|
flickerstreak@1
|
146 if cMatch == this.curMatch then
|
flickerstreak@1
|
147 this.lMatch = m
|
flickerstreak@1
|
148 this.curMatch = this.curMatch + 1
|
flickerstreak@1
|
149 matched = true
|
flickerstreak@1
|
150 break
|
flickerstreak@1
|
151 end
|
flickerstreak@1
|
152 end
|
flickerstreak@1
|
153 end
|
flickerstreak@1
|
154 end
|
flickerstreak@1
|
155 if not matched then
|
flickerstreak@1
|
156 this.curMatch = 1
|
flickerstreak@1
|
157 this.lMatch = this.origWord
|
flickerstreak@1
|
158 end
|
flickerstreak@1
|
159 this:HighlightText(pos - this.pMatchLen, pos)
|
flickerstreak@1
|
160 this:Insert(this.lMatch)
|
flickerstreak@1
|
161 end
|
flickerstreak@1
|
162
|
flickerstreak@1
|
163 function AceTab:OnTabPressed()
|
flickerstreak@1
|
164 local ost = this:GetScript("OnTextSet")
|
flickerstreak@1
|
165 if type(ost) ~= "function" then
|
flickerstreak@1
|
166 ost = nil
|
flickerstreak@1
|
167 end
|
flickerstreak@1
|
168 if ost then this:SetScript("OnTextSet", nil) end
|
flickerstreak@1
|
169 if this:GetText() == "" then return true end
|
flickerstreak@1
|
170 this:Insert("\255")
|
flickerstreak@1
|
171 pos = string.find(this:GetText(), "\255", 1) - 1
|
flickerstreak@1
|
172 this:HighlightText(pos, pos+1)
|
flickerstreak@1
|
173 this:Insert("\0")
|
flickerstreak@1
|
174 if ost then this:SetScript("OnTextSet", ost) end
|
flickerstreak@1
|
175 local fulltext = this:GetText()
|
flickerstreak@1
|
176 local text = string.sub(fulltext, 0, pos) or ""
|
flickerstreak@1
|
177
|
flickerstreak@1
|
178 local left = string.find(string.sub(text, 1, pos), "%w+$")
|
flickerstreak@1
|
179 left = left and left-1 or pos
|
flickerstreak@1
|
180 if not left or left == 1 and string.sub(text, 1, 1) == "/" then return true end
|
flickerstreak@1
|
181
|
flickerstreak@1
|
182 local _, _, word = string.find(string.sub(text, left, pos), "(%w+)")
|
flickerstreak@1
|
183 word = word or ""
|
flickerstreak@1
|
184 this.lMatch = this.curMatch > 0 and (this.lMatch or this.origWord)
|
flickerstreak@1
|
185
|
flickerstreak@1
|
186 if this.lMatch and this.lMatch ~= "" and string.find(string.sub(text, 1, pos), this.lMatch.."$") then
|
flickerstreak@1
|
187 return CycleTab()
|
flickerstreak@1
|
188 else
|
flickerstreak@1
|
189 this.matches = {}
|
flickerstreak@1
|
190 this.curMatch = 0
|
flickerstreak@1
|
191 this.lMatch = nil
|
flickerstreak@1
|
192 end
|
flickerstreak@1
|
193
|
flickerstreak@1
|
194 local completions = {}
|
flickerstreak@1
|
195 local numMatches = 0
|
flickerstreak@1
|
196 local firstMatch, hasNonFallback
|
flickerstreak@1
|
197
|
flickerstreak@1
|
198 for desc, entry in pairs(AceTab.registry) do
|
flickerstreak@1
|
199 for _, s in pairs(entry) do
|
flickerstreak@1
|
200 for _, f in pairs(s.frames) do
|
flickerstreak@1
|
201 if _G[f] == this then
|
flickerstreak@1
|
202 for _, regex in ipairs(s.patterns) do
|
flickerstreak@1
|
203 local cands = {}
|
flickerstreak@1
|
204 if string.find(string.sub(text, 1, left), regex.."$") then
|
flickerstreak@1
|
205 local c = s.wlfunc(cands, fulltext, left)
|
flickerstreak@1
|
206 if c ~= false then
|
flickerstreak@1
|
207 local mtemp = {}
|
flickerstreak@1
|
208 this.matches[desc] = this.matches[desc] or {}
|
flickerstreak@1
|
209 for _, cand in ipairs(cands) do
|
flickerstreak@1
|
210 if string.find(string.lower(cand), string.lower(word), 1, 1) == 1 then
|
flickerstreak@1
|
211 mtemp[cand] = true
|
flickerstreak@1
|
212 numMatches = numMatches + 1
|
flickerstreak@1
|
213 if numMatches == 1 then firstMatch = cand end
|
flickerstreak@1
|
214 end
|
flickerstreak@1
|
215 end
|
flickerstreak@1
|
216 for i in pairs(mtemp) do
|
flickerstreak@1
|
217 table.insert(this.matches[desc], i)
|
flickerstreak@1
|
218 end
|
flickerstreak@1
|
219 this.matches[desc].usage = s.usage
|
flickerstreak@1
|
220 if regex ~= "" and this.matches[desc][1] then
|
flickerstreak@1
|
221 hasNonFallback = true
|
flickerstreak@1
|
222 this.matches[desc].notFallback = true
|
flickerstreak@1
|
223 end
|
flickerstreak@1
|
224 end
|
flickerstreak@1
|
225 end
|
flickerstreak@1
|
226 end
|
flickerstreak@1
|
227 end
|
flickerstreak@1
|
228 end
|
flickerstreak@1
|
229 end
|
flickerstreak@1
|
230 end
|
flickerstreak@1
|
231
|
flickerstreak@1
|
232 local _, set = next(this.matches)
|
flickerstreak@1
|
233 if not set or numMatches == 0 and not hasNonFallback then return true end
|
flickerstreak@1
|
234
|
flickerstreak@1
|
235 this:HighlightText(left, left + string.len(word))
|
flickerstreak@1
|
236 if numMatches == 1 then
|
flickerstreak@1
|
237 this:Insert(firstMatch)
|
flickerstreak@1
|
238 this:Insert(" ")
|
flickerstreak@1
|
239 else
|
flickerstreak@1
|
240 if this.curMatch == 0 then
|
flickerstreak@1
|
241 this.curMatch = 1
|
flickerstreak@1
|
242 this.origWord = word
|
flickerstreak@1
|
243 this.lMatch = word
|
flickerstreak@1
|
244 CycleTab()
|
flickerstreak@1
|
245 end
|
flickerstreak@1
|
246 local gcs
|
flickerstreak@1
|
247 for h, c in pairs(this.matches) do
|
flickerstreak@1
|
248 if hasNonFallback and not c.notFallback then break end
|
flickerstreak@1
|
249 local u = c.usage
|
flickerstreak@1
|
250 c.usage = nil
|
flickerstreak@1
|
251 local candUsage = u and {}
|
flickerstreak@1
|
252 local gcs2
|
flickerstreak@1
|
253 if next(c) then
|
flickerstreak@1
|
254 if not u then DEFAULT_CHAT_FRAME:AddMessage(h..":") end
|
flickerstreak@1
|
255 for _, m in ipairs(c) do
|
flickerstreak@1
|
256 if not u then DEFAULT_CHAT_FRAME:AddMessage(m) end
|
flickerstreak@1
|
257 gcs2 = GCS(gcs2, m)
|
flickerstreak@1
|
258 end
|
flickerstreak@1
|
259 end
|
flickerstreak@1
|
260 gcs = GCS(gcs, gcs2)
|
flickerstreak@1
|
261 if u then
|
flickerstreak@1
|
262 if type(u) == "function" then
|
flickerstreak@1
|
263 local us = u(candUsage, c, gcs2, string.sub(text, 1, left))
|
flickerstreak@1
|
264 if candUsage and next(candUsage) then us = candUsage end
|
flickerstreak@1
|
265 if type(us) == "string" then
|
flickerstreak@1
|
266 DEFAULT_CHAT_FRAME:AddMessage(us)
|
flickerstreak@1
|
267 elseif type(us) == "table" and numMatches > 0 then
|
flickerstreak@1
|
268 for _, v in ipairs(c) do
|
flickerstreak@1
|
269 if us[v] then DEFAULT_CHAT_FRAME:AddMessage(string.format("%s - %s", v, us[v])) end
|
flickerstreak@1
|
270 end
|
flickerstreak@1
|
271 end
|
flickerstreak@1
|
272 end
|
flickerstreak@1
|
273 end
|
flickerstreak@1
|
274 end
|
flickerstreak@1
|
275 if curMatch == 0 then
|
flickerstreak@1
|
276 this:Insert(gcs or word)
|
flickerstreak@1
|
277 end
|
flickerstreak@1
|
278 end
|
flickerstreak@1
|
279 end
|
flickerstreak@1
|
280
|
flickerstreak@1
|
281 function AceTab:AceEvent_FullyInitialized()
|
flickerstreak@1
|
282 for frame in pairs(hookedFrames) do
|
flickerstreak@1
|
283 local Gframe = _G[frame]
|
flickerstreak@1
|
284 if not framesHooked[Gframe] then
|
flickerstreak@1
|
285 framesHooked[Gframe] = true
|
flickerstreak@1
|
286 local orig = Gframe:GetScript("OnTabPressed")
|
flickerstreak@1
|
287 if type(orig) ~= "function" then
|
flickerstreak@1
|
288 orig = function() end
|
flickerstreak@1
|
289 end
|
flickerstreak@1
|
290 Gframe:SetScript("OnTabPressed", function()
|
flickerstreak@1
|
291 if self:OnTabPressed(Gframe) then
|
flickerstreak@1
|
292 return orig()
|
flickerstreak@1
|
293 end
|
flickerstreak@1
|
294 end)
|
flickerstreak@1
|
295 Gframe.curMatch = 0
|
flickerstreak@1
|
296 Gframe.matches = {}
|
flickerstreak@1
|
297 Gframe.pMatchLen = 0
|
flickerstreak@1
|
298 end
|
flickerstreak@1
|
299 end
|
flickerstreak@1
|
300 end
|
flickerstreak@1
|
301
|
flickerstreak@1
|
302 local function external(self, major, instance)
|
flickerstreak@1
|
303 if major == "AceEvent-2.0" then
|
flickerstreak@1
|
304 if not AceEvent then
|
flickerstreak@1
|
305 AceEvent = instance
|
flickerstreak@1
|
306
|
flickerstreak@1
|
307 AceEvent:embed(self)
|
flickerstreak@1
|
308 end
|
flickerstreak@1
|
309 end
|
flickerstreak@1
|
310 end
|
flickerstreak@1
|
311
|
flickerstreak@1
|
312 local function activate(self, oldLib, oldDeactivate)
|
flickerstreak@1
|
313 if oldLib then
|
flickerstreak@1
|
314 self.registry = oldLib.registry
|
flickerstreak@1
|
315 end
|
flickerstreak@1
|
316
|
flickerstreak@1
|
317 if not self.registry then
|
flickerstreak@1
|
318 self.registry = {}
|
flickerstreak@1
|
319 end
|
flickerstreak@1
|
320
|
flickerstreak@1
|
321 if oldDeactivate then
|
flickerstreak@1
|
322 oldDeactivate(oldLib)
|
flickerstreak@1
|
323 end
|
flickerstreak@1
|
324 end
|
flickerstreak@1
|
325
|
flickerstreak@1
|
326 AceLibrary:Register(AceTab, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
|
flickerstreak@1
|
327 AceTab = AceLibrary(MAJOR_VERSION)
|