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