Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- declare("tab_complete", {})
- local Names = {}
- local NamesReverse = {}
- tab_complete.Names = Names -- TEST only here for testing purposes
- tab_complete.NamesReverse = NamesReverse -- TEST only here for testing purposes
- local function sort_func(a, b) return a:lower() < b:lower() end
- function tab_complete:OnEvent(event, data)
- local name = data.name
- -- Currently only storing a name if it's not been seen.
- -- Should the timestamp of when a name is seen be updated each time to allow for list pruning?
- if (not NamesReverse[name]) then
- local first_char = name:sub(1,1):lower()
- if (not Names[first_char]) then Names[first_char] = {} end
- table.insert(Names[first_char], tostring(name))
- NamesReverse[name] = true
- end
- end
- RegisterEvent(tab_complete, "CHAT_MSG_CHANNEL")
- RegisterEvent(tab_complete, "CHAT_MSG_CHANNEL_ACTIVE")
- RegisterEvent(tab_complete, "CHAT_MSG_CHANNEL_EMOTE")
- RegisterEvent(tab_complete, "CHAT_MSG_CHANNEL_EMOTE_ACTIVE")
- -- tabcomplete(<string>, <number>)
- -- returns the entire string regardless of modification
- -- nextname(<string>, <boolean>)
- -- returns the next name eligible for tab-completion or nil
- -- isn't a drop-in replacement for tabcomplete().
- -- instead it will do the work of getting the name if one is available.
- -- It can also work as an iterator/generator for use in a for loop if any value is passed to the second argument
- local last_part
- local last_part_prev_match -- matches are stored lowercase
- -- Because of the volatility of the list order as names are seen, we can't store index numbers for quick reference.
- -- So it is necessary to iterate over all names in the list to find the next match. The previous match is stored
- -- for this purpose. Inside of the function, the index of the first match will be retained in case the first match
- -- after the previous match is out of scope.
- local function iterate_names(part, index)
- if (not index and last_part ~= part) then
- last_part = part
- last_part_prev_match = nil
- end
- local first_char = part:sub(1,1):lower() -- part should be guaranteed to be <string> since it's vetted in nextname()
- local Names = Names[first_char]
- if (not Names) then return end
- local part_len = part:len()
- local name_index
- local first_index
- local stored_name
- for i = (index or 0) + 1, #Names do
- stored_name = Names[i]:lower()
- local stored_name_part = stored_name:sub(1, part_len)
- if (stored_name_part == part) then
- if (not first_index) then first_index = i end
- if (stored_name > (not index and last_part_prev_match or "")) then
- name_index = i
- break
- end
- elseif (stored_name_part > part) then
- if (not index) then
- name_index = first_index
- end
- break
- end
- end
- if (index) then
- return name_index, Names[name_index]
- else
- name_index = name_index or first_index
- last_part_prev_match = Names[name_index] and Names[name_index]:lower()
- return Names[name_index]
- end
- end
- function tab_complete:nextname(part, list)
- if (type(part) == "number") then part = tostring(part) end
- assert(type(part) == "string", 'Error! Needs String') -- TODO better error message
- part = part:lower()
- -- Sort the list here instead of when names are added to reduce potential latency during combat
- -- One thing to consider, since the sort is only being done at the time of use on the small subset of names starting with
- -- the first character, would it be worthwhile updating list positions on these names and storing it in the reverse lookup
- -- table used to verify existence? Doing this would also allow for cleaning up the output during use in a for loop.
- local first_char = part:sub(1,1):lower()
- if (self.Names[first_char]) then table.sort(self.Names[first_char], sort_func) end
- -- Could abort now if the table is non-existant, but depending on how the function is being called
- -- requires different responses. It's simpler to just let it run and respond naturally.
- if (list) then
- return iterate_names, part, 0 -- starting at 0 each time because we currently aren't updating list positions on sort
- else
- return iterate_names(part)
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement