diff --git a/.gitignore b/.gitignore index dad02d1..f535252 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ out/ obj/ # Misc +.bin/ *.log *.graphml coverage.db* diff --git a/.pkgmeta b/.pkgmeta index 522e75c..273c52d 100644 --- a/.pkgmeta +++ b/.pkgmeta @@ -6,7 +6,10 @@ externals: libs/LibStub: https://repos.wowace.com/wow/libstub/tags/1.0 libs/CallbackHandler-1.0: https://repos.wowace.com/wow/callbackhandler/trunk/CallbackHandler-1.0 libs/AceAddon-3.0: https://repos.wowace.com/wow/ace3/trunk/AceAddon-3.0 + libs/AceConfig-3.0: https://repos.wowace.com/wow/ace3/trunk/AceConfig-3.0 libs/AceConsole-3.0: https://repos.wowace.com/wow/ace3/trunk/AceConsole-3.0 + libs/AceDB-3.0: https://repos.wowace.com/wow/ace3/trunk/AceDB-3.0 + libs/AceDBOptions-3.0: https://repos.wowace.com/wow/ace3/trunk/AceDBOptions-3.0 libs/AceEvent-3.0: https://repos.wowace.com/wow/ace3/trunk/AceEvent-3.0 libs/AceGUI-3.0: https://repos.wowace.com/wow/ace3/trunk/AceGUI-3.0 libs/AceHook-3.0: https://repos.wowace.com/wow/ace3/trunk/AceHook-3.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f483c..f412c0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,15 +3,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] Version 1.0 - 2020-06-02 +## [Unreleased] Version 0.7.0-beta - 2020-06-06 ### Added -- case sensitivity +- handle replacement via slash command + +## Version 0.6.0 - 2020-06-05 +### Added +- honour capital/mixed cases for ignore case - consolidate consecutive matches +- database upgrade capability +### Changed +- case sensitivity option reversed +- removed obsolete WIM handling +### Fixed +- skip empty mapping +- Deletion of mapping ## Version 0.5.0 - 2020-06-01 ### Added - add replacements via options UI -- handle replacement via slash command ## Version 0.4.0 - 2020-05-30 ### Added diff --git a/Grichelde.lua b/Grichelde.lua index 8467389..6825fd3 100644 --- a/Grichelde.lua +++ b/Grichelde.lua @@ -1,17 +1,15 @@ --[[--------------------------------------------------------------------------- - Grichelde - Copyright 2020 Teilzeit-Jedi - based on Misspelled developed by Nathan Pieper - nrpieper (@) gmail (dot) com + Grichelde - Text Replacer + Copyright 2020 Teilzeit-Jedi - This code freely distributed for your use in any GPL compliant project. - See conditions in the LICENSE file attached. + This addon is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + You should have received a copy of the GNU General Public License + along with the addon. If not, see . -----------------------------------------------------------------------------]] @@ -27,6 +25,7 @@ Grichelde.build = GetAddOnMetadata(AddonName, "X-Build") or "Experimental" Grichelde.hooks = {} Grichelde.classic = _G.WOW_PROJECT_ID == _G.WOW_PROJECT_CLASSIC Grichelde.debug = false +Grichelde.trace = false -- publish to global env _G.Grichelde = Grichelde @@ -35,25 +34,21 @@ _G.Grichelde = Grichelde function Grichelde:OnInitialize() -- Build Interface Options window self.db = self:LoadDatabase() + self:UpgradeDatabase() + self.options, self.dialog = self:SetupOptions() + -- load replacements from database self:RefreshOptions("OnProfileChanged") self:DebugPrint(self.db.profile) self:SetupSlashCommands() - - -- Watch for WIM and Prat to Load, then integrate - self:RegisterEvent("ADDON_LOADED", "HookIntoForOtherChatAddons") end function Grichelde:OnEnable() -- Hook in before message is sent to replace all character occurrences where replacements have been defined in the options self:RawHook("SendChatMessage", true) - if (_G.Misspelled) then - self:PrefixedPrint(self.L.Addon_Detected_Misspelled) - end - -- tell the world we are listening self:Print(self.L.AddonLoaded, self.COLOR_CODES.PREFIX .. self.L.AddonName .. " " .. self.L.VersionAbbr .. self.version .. self.COLOR_CODES.CLOSE) end @@ -76,40 +71,4 @@ function Grichelde:SetupSlashCommands() self:RegisterChatCommand("grichelde", HandleSlashCommand) self:RegisterChatCommand("gri", HandleSlashCommand) -end - ---- Hook into WIM to catch whisper sending event. --- @param event string --- @param addonName string -function Grichelde:HookIntoForOtherChatAddons(event, addonName) - if event == "ADDON_LOADED" then - if addonName == "WIM" then - _G.WIM.RegisterWidgetTrigger("msg_box", "whisper,chat,w2w", "OnEnterPressed", Grichelde.EditBox_OnEnterPressed) - - -- If available use the WIM API - if (_G.WIM.RegisterPreSendFilterText) then -- avoid error if WIM not up to date. - _G.WIM.RegisterPreSendFilterText(function(text) - return self:CheckAndReplace(text) - end) - else - -- WIM sends its chat messages via the API ChatThrottleLib, which itself hooks the default SendChatMessage api - -- many times before Grichelde will. ChatThrottleLib might potentially load before Grichelde, so we just hook - -- into ChatThrottleLib to be on the safe side. - - if (_G.ChatThrottleLib) then - self.hooks["ChatThrottleLib"] = _G.ChatThrottleLib.SendChatMessage - - function _G.ChatThrottleLib:SendChatMessage(prio, prefix, text, ...) - Grichelde:DebugPrint("ChatThrottleLib:SendChatMessage : Hook called") - local replacedText = Grichelde:CheckAndReplace(text) - return Grichelde.hooks["ChatThrottleLib"](_G.ChatThrottleLib, prio, prefix, replacedText, ...) - end - end - end - - if (_G.WIM) then - self:PrefixedPrint(self.L.Addon_Detected_WIM) - end - end - end end \ No newline at end of file diff --git a/Grichelde.toc b/Grichelde.toc index 488f1e9..0c614f5 100644 --- a/Grichelde.toc +++ b/Grichelde.toc @@ -1,14 +1,15 @@ ## Interface: 11304 ## Title: Grichelde -## Notes: Replaces characters you type in the chat box +## Notes: Replaces characters from the chat box ## Notes-de: Ersetzt eingegebene Zeichen in der Chat-Zeile -## Version: 0.5.0 +## Version: 0.6.0 ## Author: Teilzeit-Jedi ## eMail: tj@teilzeit-jedi.de ## X-Build: Classic ## X-Curse-Project-ID: 385480 +## X-License: GPLv3 ## X-Category: Chat/Communication ## X-Credits: Teilzeit-Jedi, Nathan Pieper ## X-Embeds: Ace3 @@ -23,5 +24,6 @@ Grichelde.lua GricheldeConstants.lua GricheldeUtils.lua GricheldeDatabase.lua +GricheldeUpgrade.lua GricheldeOptions.lua GricheldeChat.lua diff --git a/GricheldeChat.lua b/GricheldeChat.lua index 5f0f178..b236004 100644 --- a/GricheldeChat.lua +++ b/GricheldeChat.lua @@ -2,8 +2,9 @@ local _G = _G local Grichelde = _G.Grichelde -local nilOrEmpty, ipairs, tContains, tFilter, tInsert, tConcat, find, sub, gsub, toLower, trim, length - = Grichelde.functions.nilOrEmpty, Grichelde.functions.ipairs, Grichelde.functions.tContains, Grichelde.functions.tFilter, Grichelde.functions.tInsert, Grichelde.functions.tConcat, Grichelde.functions.find, Grichelde.functions.sub, Grichelde.functions.gsub, Grichelde.functions.toLower, Grichelde.functions.trim, Grichelde.functions.length +local nilOrEmpty, ipairs, spairs, tContains, tFilter, tInsert, tConcat, find, sub, isUpper, isLower, toUpper, toLower, trim, length + = Grichelde.functions.nilOrEmpty, Grichelde.functions.ipairs, Grichelde.functions.spairs, Grichelde.functions.tContains, Grichelde.functions.tFilter, Grichelde.functions.tInsert, Grichelde.functions.tConcat, + Grichelde.functions.find, Grichelde.functions.sub, Grichelde.functions.isUpper, Grichelde.functions.isLower, Grichelde.functions.toUpper, Grichelde.functions.toLower, Grichelde.functions.trim, Grichelde.functions.length --- Before a chat message is sent, check if replacement is required and replace the text accordingly. -- @param message string @@ -13,7 +14,6 @@ local nilOrEmpty, ipairs, tContains, tFilter, tInsert, tConcat, find, sub, gsub, function Grichelde:SendChatMessage(message, type, language, channel, ...) local replacedText = self:CheckAndReplace(message, type) - self:DebugPrint("SendChatMessage : replacedText:", replacedText) -- Send text in chunks if length exceeds 255 bytes after replacement local chunks = self:SplitText(replacedText) @@ -55,7 +55,7 @@ function Grichelde:CheckReplacement(text, channel) ) self:DebugPrint("CheckReplacement : allowed channels:", tConcat(allowedChannels, ", ")) local type = self:ConvertBlizChannelToType(channel) - if (type ~= nil and not tContains(allowedChannels, type)) then + if (type == nil or not tContains(allowedChannels, type)) then self:DebugPrint("CheckReplacement : skip channel type:", type) return false end @@ -103,7 +103,7 @@ function Grichelde:ReplaceText(text) while current <= length(newText) do local currentChar = sub(newText, current, current) - self:DebugPrint("current/char : %s,%s", current, currentChar) + self:TracePrint("current/char : %s,%s", current, currentChar) if ( not tContains(lookAheads, currentChar)) then current = current + 1 @@ -194,17 +194,181 @@ end -- @param text string -- @return string function Grichelde:ReplaceCharacters(text) - -- todo: read from options - -- todo: case (in)sensitivity - -- todo: consolidate consecutive - -- todo: prevent infinite loops - is that even possible? - local replacement = text - replacement = gsub(replacement, "s", "ch") - replacement = gsub(replacement, "S", "Ch") - replacement = gsub(replacement, "t", "k") - replacement = gsub(replacement, "T", "K") - self:DebugPrint("ReplaceCharacters : replaced \"%s\" with \"%s\"", text, replacement) - return replacement + local replacements = self.db.profile.replacements or {} + self:DebugPrint("ReplaceCharacters : replacements") + self:DebugPrint(replacements) + + local result = text + local consolidate = {} + + -- replacements are done first + for replName, replTable in spairs(replacements) do + local before = result + local search = replTable.searchText + + if not nilOrEmpty(search) then + local replace = replTable.replaceText + consolidate[replName] = {} + + if replTable.ignoreCase then + self:DebugPrint("ReplaceCharacters : \"%s => %s\" (ignoreCase)", search, replace) + local pos, offset = 1, 0 + local lowerResult = toLower(result) + local lowerSearch = toLower(search) + + local pos1, pos2 = find(lowerResult, lowerSearch, pos) + while (pos1 and pos2) do + self:TracePrint("pos1: %d, pos2: %d", pos1, pos2) + local pre = sub(result, 1, pos1 - 1 + offset) + local match = sub(result, pos1 + offset, pos2 + offset) + local post = sub(result, pos2 + 1 + offset) + self:TracePrint("pre: %s, match: %s, post: %s", pre, match, post) + + -- keep cases + local repl = "" + local lastCase = nil + for p = pos1, pos2 do + self:TracePrint("p: %d", p) + local c = sub(match, p - pos1 + 1, p - pos1 + 1) + local r = sub(replace, p - pos1 + 1, p - pos1 + 1) or "" + + if (isUpper(c)) then -- all UPPER-CASE letter + lastCase = true + repl = repl .. toUpper(r) + elseif (isLower(match)) then -- all lower_case letter + lastCase = false + repl = repl .. toLower(r) + else -- no letter + lastCase = nil + repl = repl .. r + end + self:TracePrint("character: %s, %s", c, r) + end + + self:TracePrint("length %d > %d", length(replace), pos2 - pos1 + 1) + if (length(replace) > pos2 - pos1 + 1) then + local remainingReplace = sub(replace, pos2 - pos1 + 2) + local nextLetter = sub(post, 1, 1) + + self:TracePrint("rest: %s, n: %s, lastCase: %s", remainingReplace, nextLetter, lastCase) + + if (isUpper(nextLetter)) then + repl = repl .. toUpper(remainingReplace) + elseif (isLower(nextLetter)) then + repl = repl .. toLower(remainingReplace) + else + -- no letter + repl = repl .. remainingReplace +-- if lastCase == nil then +-- repl = repl .. remainingReplace +-- elseif lastCase == false then +-- repl = repl .. toLower(remainingReplace) +-- else +-- repl = repl .. toUpper(remainingReplace) +-- end + end + end + + -- actual replacement + result = pre .. repl .. post + self:DebugPrint("result: %s", result) + + -- remember positions for consolidate + if replTable.consolidate then + tInsert(consolidate[replName], pos1 + offset) + end + + -- replacement text can be longer or shorter the resulting text + -- after replacement result and lowerResult can have different sizes + offset = offset + length(repl) - length(lowerSearch) + -- update values for next iteration + pos = pos2 + 1 + pos1, pos2 = find(lowerResult, lowerSearch, pos) + end + else + -- exact case + self:DebugPrint("ReplaceCharacters : \"%s => %s\" (exact case)", search, replace) + local pos, offset = 1, 0 + local oldResult = result + + local pos1, pos2 = find(oldResult, search, pos) + while (pos1 and pos2) do + self:TracePrint("pos1: %d, pos2: %d", pos1, pos2) + local pre = sub(result, 1, pos1 - 1 + offset) + local post = sub(result, pos2 + 1 + offset) + self:TracePrint("pre: %s, post: %s", pre, post) + + -- actual replacement + result = pre .. replace .. post + self:DebugPrint("result: %s", result) + + -- remember positions for consolidate + if replTable.consolidate then + tInsert(consolidate[replName], pos1 + offset) + end + + -- replacement text can lengthen or shorten the resulting text + -- after replacement result and lowerResult can have different sizes + offset = offset + length(replace) - length(search) + -- update values for next iteration + pos = pos2 + 1 + pos1, pos2 = find(oldResult, search, pos) + end + end + + if before ~= result then + self:DebugPrint("ReplaceCharacters : replaced \"%s\" with \"%s\"", before, result) + end + else + self:DebugPrint("ReplaceCharacters : Skip replacement for empty mapping") + end + end + + -- consolidation is done last + for replName, replTable in spairs(replacements) do + local before = result + --local search = replTable.searchText + local search = replTable.searchText + + if not nilOrEmpty(search) then + local replace = replTable.replaceText + local lowerResult = toLower(result) + local offset = 0 + + if replTable.consolidate then + self:DebugPrint("ReplaceCharacters : consolidating \"%s => %s\"", search, replace) + self:DebugPrint("consolidate[" .. replName .. "] is:") + self:DebugPrint(consolidate[replName]) + + for _, pos1 in spairs(consolidate[replName]) do + local pos2 = pos1 + length(replace) - 1 + self:TracePrint("pos1: %d, pos2: %d", pos1, pos2) + local match = sub(lowerResult, pos1, pos2) + local next = sub(lowerResult, pos2 + 1, pos2 + 1 + pos2 - pos1) + self:TracePrint("match: %s, next: %s", match, next) + + local _, p2 = find(next, "^" .. match) + self:TracePrint("p2: %d", p2) + if (p2) then + result = sub(result, 1, pos2 + offset) .. sub(result, pos2 + 1 + p2 + offset) + + -- consolidation will shorten the resulting text + offset = offset + length(result) - length(lowerResult) + end + self:DebugPrint("result: %s", result) + end + end + + if before ~= result then + self:DebugPrint("ReplaceCharacters : consolidate \"%s\" with \"%s\"", before, result) + end + else + self:DebugPrint("ReplaceCharacters : Skip consolidation for empty mapping") + end + end + + self:DebugPrint("ReplaceCharacters : final text:", result) + return result end --- Splits a long text in longest possible chunks of <= 255 length, split at last available space diff --git a/GricheldeConstants.lua b/GricheldeConstants.lua index 58f85c1..212e1e7 100644 --- a/GricheldeConstants.lua +++ b/GricheldeConstants.lua @@ -12,7 +12,9 @@ Grichelde.COLOR_CODES.NORMAL = _G.NORMAL_FONT_COLOR_CODE or "|cffffd20 Grichelde.COLOR_CODES.HIGHLIGHT = _G.HIGHLIGHT_FONT_COLOR_CODE or "|cffffffff"; Grichelde.COLOR_CODES.RED = _G.RED_FONT_COLOR_CODE or "|cffff2020"; Grichelde.COLOR_CODES.GREEN = _G.GREEN_FONT_COLOR_CODE or "|cff20ff20"; +Grichelde.COLOR_CODES.LIGHTGRAY = "|cffC0C0C0"; Grichelde.COLOR_CODES.GRAY = _G.GRAY_FONT_COLOR_CODE or "|cff808080"; +Grichelde.COLOR_CODES.DARKGRAY = "|cff404040"; Grichelde.COLOR_CODES.YELLOW = _G.YELLOW_FONT_COLOR_CODE or "|cffffff00"; Grichelde.COLOR_CODES.LIGHTYELLOW = _G.LIGHTYELLOW_FONT_COLOR_CODE or "|cffffff9a"; Grichelde.COLOR_CODES.ORANGE = _G.ORANGE_FONT_COLOR_CODE or "|cffff7f3f"; @@ -24,7 +26,7 @@ local function nilOrEmpty(s) return s == nil or s:trim() == "" end -local function spairs(t , orderFunc) +local function spairs(t, orderFunc) -- collect the keys local sortedKeys = {} -- for every non-nil value @@ -35,6 +37,7 @@ local function spairs(t , orderFunc) if orderFunc then Grichelde.functions.tSort(sortedKeys, function(a, b) return orderFunc(sortedKeys, a, b) end) else + -- lexicographical order Grichelde.functions.tSort(sortedKeys) end @@ -86,6 +89,28 @@ local function tClone(orig) return copy end +local function isChar(char) + return Grichelde.functions.find(char, "%a+") +end + +local function isNumber(digit) + return Grichelde.functions.find(Grichelde.functions.toString(digit), "%d+") +end + +local function isUpper(char) + return Grichelde.functions.isChar(char) and char == Grichelde.functions.toUpper(char) +end + +local function isLower(char) + return Grichelde.functions.isChar(char) and char == Grichelde.functions.toLower(char) +end + +local function isCapital(word) + return Grichelde.functions.legnth(word) > 1 + and Grichelde.functions.isUpper(Grichelde.functions.sub(word,1,1)) + and Grichelde.functions.isLower(Grichelde.functions.sub(word,2)) +end + -- faster function lookups by mapping to local refs Grichelde.functions = {} Grichelde.functions.type = _G.type @@ -112,8 +137,13 @@ Grichelde.functions.gsub = _G.string.gsub Grichelde.functions.match = _G.strmatch Grichelde.functions.join = _G.strjoin Grichelde.functions.split = _G.strsplit -Grichelde.functions.toLower = _G.strlower Grichelde.functions.toUpper = _G.strupper +Grichelde.functions.toLower = _G.strlower +Grichelde.functions.isChar = isChar +Grichelde.functions.isNumber = isNumber +Grichelde.functions.isUpper = isUpper +Grichelde.functions.isLower = isLower +Grichelde.functions.isCapital = isCapital Grichelde.functions.format = _G.string.format Grichelde.functions.rep = _G.string.rep Grichelde.functions.trim = _G.strtrim diff --git a/GricheldeDatabase.lua b/GricheldeDatabase.lua index 48aaadc..2bb8ea3 100644 --- a/GricheldeDatabase.lua +++ b/GricheldeDatabase.lua @@ -2,50 +2,52 @@ local _G = _G local Grichelde = _G.Grichelde -local pairs, ipairs, tInsert, tSort, unpack, join, toString - = Grichelde.functions.pairs, Grichelde.functions.ipairs, Grichelde.functions.tInsert, Grichelde.functions.tSort, Grichelde.functions.unpack, Grichelde.functions.join, Grichelde.functions.toString +local spairs, unpack, join, toString + = Grichelde.functions.spairs, Grichelde.functions.unpack, Grichelde.functions.join, Grichelde.functions.toString -local defaultConfig = { - global = {}, - profile = { - enabled = true, - channels = { - ["*"] = false, - say = true, - emote = false, - yell = true, - party = true, - guild = true, - officer = true, - }, - replacements = { - ["**"] = { - order = 9999, - searchText = "", - replaceText = "", - caseSensitive = false, - consolidate = true, +function Grichelde:GetDefaultConfig() + return { + global = {}, + profile = { + enabled = true, + channels = { + ["*"] = false, + say = true, + emote = false, + yell = true, + party = true, + guild = true, + officer = true, }, - replacement_0 = { - order = 5, - searchText = "s", - replaceText = "ch", - caseSensitive = false, - consolidate = true, - }, - replacement_1 = { - order = 9, - searchText = "t", - replaceText = "ck", - caseSensitive = false, - consolidate = true, + replacements = { + ["**"] = { + order = 9999, + searchText = "", + replaceText = "", + ignoreCase = true, + consolidate = true, + }, + replacement_0 = { + order = 5, + searchText = "s", + replaceText = "ch", + ignoreCase = true, + consolidate = true, + }, + replacement_1 = { + order = 9, + searchText = "t", + replaceText = "ck", + ignoreCase = true, + consolidate = true, + } } } } -} +end function Grichelde:LoadDatabase() - local db = LibStub("AceDB-3.0"):New(self.name .."DB", defaultConfig, true) + local db = LibStub("AceDB-3.0"):New(self.name .."DB", self:GetDefaultConfig(), true) db.RegisterCallback(self, "OnNewProfile", "RefreshOptions") db.RegisterCallback(self, "OnProfileChanged", "RefreshOptions") @@ -76,7 +78,7 @@ function Grichelde:ReadFromDatabase(info) path = path + 1 end local optionPath = join(".", unpack(info, 1, #info)) - self:DebugPrint("read option \"%s\": %s", optionPath, toString(option)) + self:TracePrint("read option \"%s\": %s", optionPath, toString(option)) return option end @@ -86,37 +88,19 @@ end -- @return table function Grichelde:ReorderReplacements(replacementsTable) local replacements = replacementsTable or {} - local sortedByOrder = {} - for replName, _ in pairs(replacements) do - tInsert(sortedByOrder, replName) - end - - tSort(sortedByOrder) -- lexicographical order will do for non-nil values - --[[tSort(sortedByOrder, function(a, b) - self:DebugPrint("ReorderReplacements : sort ", a, b) - if a then - if b then - return a < b - else - return a - end - else - return b - end - end)]] - - self:DebugPrint("ReorderReplacements : sortedByOrder") - self:DebugPrint(sortedByOrder) + self:TracePrint("ReorderReplacements : unsorted table") + self:TracePrint(replacementsTable) local sortedReplacements = {} + local index = 0 - for _, replName in ipairs(sortedByOrder) do - sortedReplacements["replacement_"..index] = replacements[replName] - sortedReplacements["replacement_"..index].order = index + for _, replTable in spairs(replacements) do + sortedReplacements["replacement_" .. index] = replTable + sortedReplacements["replacement_" .. index].order = index index = index + 1 end - --self:DebugPrint("ReorderReplacements : sorted table") - --self:DebugPrint(sortedReplacements) + self:TracePrint("ReorderReplacements : sorted table") + self:TracePrint(sortedReplacements) return sortedReplacements end \ No newline at end of file diff --git a/GricheldeOptions.lua b/GricheldeOptions.lua index 39e8471..5960493 100644 --- a/GricheldeOptions.lua +++ b/GricheldeOptions.lua @@ -147,11 +147,11 @@ function Grichelde:CreateReplacement(offset) name = self.L.Options_Replacement_ReplaceText_Name, desc = self.L.Options_Replacement_ReplaceText_Desc, }, - caseSensitive = { + ignoreCase = { order = 2, type = "toggle", - name = self.L.Options_Replacement_CaseSensitive_Name, - desc = self.L.Options_Replacement_CaseSensitive_Desc, + name = self.L.Options_Replacement_IgnoreCase_Name, + desc = self.L.Options_Replacement_IgnoreCase_Desc, }, consolidate = { order = 3, @@ -225,15 +225,16 @@ function Grichelde:RefreshOptions(event) self:DebugPrint("Refreshing Profile %s on options change: %s", self.db:GetCurrentProfile(), event) end - self:RefreshReplacements(self:ReorderReplacements(self.db.profile.replacements)) + self.db.profile.replacements = self:ReorderReplacements(self.db.profile.replacements) + self:RefreshReplacements(self.db.profile.replacements) end --- Create UI options for rhe given replacement table (from DB). --- Usually called with with self.db.profile.replacements -- @param replacementsTable function Grichelde:RefreshReplacements(replacementsTable) - --self:DebugPrint("RefreshReplacements : DB table:") - --self:DebugPrint(replacementsTable) + self:TracePrint("RefreshReplacements : DB table:") + self:TracePrint(replacementsTable) -- remove all previous replacements from options (not DB), except header and buttons local replacements = self.options.args.replacements.args or {} @@ -248,22 +249,25 @@ function Grichelde:RefreshReplacements(replacementsTable) replacements[replName] = self:CreateReplacement(toNumber(replNumber)) end - --self:DebugPrint("RefreshReplacements : UI options:") - --self:DebugPrint(replacements) + self:TracePrint("RefreshReplacements : UI options:") + self:TracePrint(replacements) self.dialog:ConfigTableChanged(nil, self.name) end function Grichelde:AddReplacement() local replacements = self.db.profile.replacements + self:DebugPrint("AddReplacements : old DB entries:") + self:DebugPrint(replacements) + local maxRepl = tSize(replacements) local newMapping = "replacement_" .. maxRepl self:DebugPrint("AddReplacement : new replacement key:", newMapping) -- setting replacements[newMapping] = {} will deactivate defaults replacements[newMapping].order = maxRepl - --self:DebugPrint("AddReplacements : all DB entries:") - --self:DebugPrint(replacements) + self:DebugPrint("AddReplacements : new DB entries:") + self:DebugPrint(replacements) --self.db.profile.replacements = replacements self:RefreshOptions("AddReplacement " .. newMapping) diff --git a/GricheldeUpgrade.lua b/GricheldeUpgrade.lua new file mode 100644 index 0000000..0a6c742 --- /dev/null +++ b/GricheldeUpgrade.lua @@ -0,0 +1,71 @@ +-- read namespace from global env +local _G = _G +local Grichelde = _G.Grichelde + +local pairs, find, toNumber = Grichelde.functions.pairs, Grichelde.functions.find, Grichelde.functions.toNumber + +function Grichelde:Upgrade_To_v060() + self:PrefixedPrint(self.L.Upgrade_ToVersion, Grichelde.COLOR_CODES.ORANGE .. "0.6.0" .. Grichelde.COLOR_CODES.CLOSE) + + local replacements = self.db.profile.replacements or {} + self:DebugPrint("Upgrade_To_060 : old database") + self:DebugPrint(replacements) + + for _, replTable in pairs(replacements) do + replTable.ignoreCase = not replTable["caseSensitive"] + replTable["caseSensitive"] = nil + end + + self:DebugPrint("Upgrade_To_060 : new database") + self:DebugPrint(replacements) + return 0, 6, 0 +end + +--[[ +function Grichelde:Upgrade_To_v061() + return 0, 6, 1 +end + +function Grichelde:Upgrade_To_v070() + return 0, 7, 0 +end +]] + +function Grichelde:UpgradeDatabase() + local dbVersion = self.db.global.version or "0.0.0" + self:DebugPrint("Database version:", dbVersion) + + local _, _, maj, min, pat = find(dbVersion, "(%d+)%.(%d+)%.(%d+).*") + local major, minor, patch = toNumber(maj) or 0, toNumber(min) or 0, toNumber(pat) or 0 + + local upgrade = 0 + local error = false + + if major == 0 then + if minor < 6 then + upgrade = upgrade + 1 + major, minor, patch = self:Upgrade_To_v060(dbVersion) + end +--[[ + if minor == 6 then + if patch < 1 then + upgrade = upgrade + 1 + major, minor, patch = self:Upgrade_To_v061(dbVersion) + end + end + if minor < 7 then + upgrade = upgrade + 1 + major, minor, patch = self:Upgrade_To_v070(dbVersion) + end +]] + end + + if upgrade == 0 then + self:DebugPrint("Database up-to-date") + else + if not error then + self.db.global.version = self.version + self:PrefixedPrint(Grichelde.COLOR_CODES.GREEN .. self.L.Upgrade_Successful .. Grichelde.COLOR_CODES.CLOSE) + end + end +end \ No newline at end of file diff --git a/GricheldeUtils.lua b/GricheldeUtils.lua index 9bec266..55c852b 100644 --- a/GricheldeUtils.lua +++ b/GricheldeUtils.lua @@ -36,9 +36,9 @@ local function tPrint(val, indent, known, printFunc) if tSize(val) > 0 then for key, value in pairs(val) do if value == nil then - printF(rep(" ", indent) .. plainValue(key) .. "= ") + printF(rep(" ", indent) .. plainValue(key) .. " = ") elseif type(value) == "table" then - printF(rep(" ", indent) .. plainValue(key) .. "= {") + printF(rep(" ", indent) .. plainValue(key) .. " = {") if tSize(value) > 0 then if not known[value] then tPrint(value, indent + 4, known, printF) @@ -65,9 +65,9 @@ function Grichelde:SplitOnFirstMatch(text, delimPattern, start) if text == nil then return nil end local pattern = "^(.-)" .. (delimPattern or " " ) .."(.*)" local pos = start or 1 - self:DebugPrint("SplitOnFirstMatch : text: %s, pattern: %s, start: %d", text, pattern, start) + self:TracePrint("SplitOnFirstMatch : text: %s, pattern: %s, start: %d", text, pattern, start) local _, _, left, right = find(text, pattern, pos) - self:DebugPrint("SplitOnFirstMatch : left: %s, right: %s", left, right) + self:TracePrint("SplitOnFirstMatch : left: %s, right: %s", left, right) return left or text, right end @@ -75,9 +75,9 @@ end function Grichelde:SplitOnLastMatch(text, delimPattern, start) local pattern = "(.*)" .. (delimPattern or " ") .. "(.-)$" local pos = start or 1 - self:DebugPrint("SplitOnLastMatch : text: %s, pattern: %s, start: %d", text, pattern, start) + self:TracePrint("SplitOnLastMatch : text: %s, pattern: %s, start: %d", text, pattern, start) local _, _, left, right = find(text, pattern, pos) - self:DebugPrint("SplitOnLastMatch : left: %s, right: %s", left, right) + self:TracePrint("SplitOnLastMatch : left: %s, right: %s", left, right) return left, right or text end @@ -153,4 +153,39 @@ function Grichelde:DebugPrint(obj, ...) end end end +end + +function Grichelde:TracePrint(obj, ...) + local function prefixedTracePrint(...) + print(self.COLOR_CODES.DARKGRAY .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...)) + end + + if (self.debug and self.trace) then + if obj == nil then + prefixedTracePrint("") + else + if type(obj) == "string" then + local l = select("#", ...) + if ( l == 0 or not find(obj, "%%")) then + prefixedTracePrint(obj, ...) + else + -- sanitize nil values in vararg + local packed = { ... } + for i = 1, l do + packed[i] = toString(packed[i]) or "nil" + end + +-- print("packed = ", packed) +-- self:tPrint(packed) + -- cannot assign unpacked to a vararg variable and print it for debug + local fmtMsg = format(obj, unpack(packed, 1, l)) -- manually set count as unpack() stops on nil (bug with #table) + prefixedTracePrint(fmtMsg) + end + elseif type(obj) == "table" then + tPrint(obj, 0, {}, prefixedTracePrint) + else + prefixedTracePrint(plainValue(obj)) + end + end + end end \ No newline at end of file diff --git a/localisation/deDE.lua b/localisation/deDE.lua index 691ce4c..af5333c 100644 --- a/localisation/deDE.lua +++ b/localisation/deDE.lua @@ -5,8 +5,8 @@ if not L then return end L.AddonName = "Grichelde" L.VersionAbbr = "v" L.AddonLoaded = "%s hilft Euch jetzt bei euren Sprachschwierigkeiten." -L.Addon_Detected_Misspelled = "Das Addon 'Misspelled' wurde erkannt und alle Nachrichten werden automatisch bereinigt." -L.Addon_Detected_WIM = "Das Addon 'WIM' wurde erkannt und alle Flüsternnachrichten aus IM-Fenster werden behandelt." +L.Upgrade_ToVersion = "Hebe Databank auf Version %s an." +L.Upgrade_Successful = "Upgrade erfolgreich." -- profiles L.Profiles_Available = "Verf\195\188gbare Profile:" @@ -68,8 +68,8 @@ L.Options_Replacement_SearchText_Name = "Suchtext:" L.Options_Replacement_SearchText_Desc = "Dieser Text wird in der Chateingabe gesucht." L.Options_Replacement_ReplaceText_Name = "Ersetzung:" L.Options_Replacement_ReplaceText_Desc = "Jeder Suchtreffer wird mit diesem Text ersetzt." -L.Options_Replacement_CaseSensitive_Name = "Gro\195\159- und Kleinschreibung beachten" -L.Options_Replacement_CaseSensitive_Desc = "Groß\195\159buchstaben werden mit Gro\195\159buchstaben ersetzt." +L.Options_Replacement_IgnoreCase_Name = "ignoriere Gro\195\159- und Kleinschreibung" +L.Options_Replacement_IgnoreCase_Desc = "Wenn nicht gesetzt, muss die Groß\195\159- und Kleinschreibung des Suchtextes exakt \195\188berein stimmen.|nDie Groß\195\159schreibung jedes Zeichens wird bei der Ersetzung \195\188bernommen." L.Options_Replacement_Consolidate_Name = "Fa\195\159e aufeinanderfolgende Treffer zusammen" L.Options_Replacement_Consolidate_Desc = "Wenn durch die Ersetzung die Zeichenfolge mehrfach hintereinander steht,|nfasse sie zu einem Vorkommen zusammen." L.Options_Replacement_Delete_Name = "L\195\182schen" diff --git a/localisation/enUS.lua b/localisation/enUS.lua index f6c293c..bdf82ee 100644 --- a/localisation/enUS.lua +++ b/localisation/enUS.lua @@ -5,9 +5,8 @@ if not L then return end L.AddonName = "Grichelde" L.VersionAbbr = "v" L.AddonLoaded = "%s now helps you with your spelling disabilities." -L.Addon_Detected_Misspelled = "The Addon 'Misspelled' has been detected and any messsage will be cleansed automatically." -L.Addon_Detected_WIM = "The Addon 'WIM' has been detected and any whispers will be handled from IM windows." - +L.Upgrade_ToVersion = "Upgrade database to version %s." +L.Upgrade_Successful = "Upgrade successful." -- profiles L.Profiles_Available = "Available profiles:" @@ -69,8 +68,8 @@ L.Options_Replacement_SearchText_Name = "Search for:" L.Options_Replacement_SearchText_Desc = "This text is looked up in your chat input box." L.Options_Replacement_ReplaceText_Name = "Replacement:" L.Options_Replacement_ReplaceText_Desc = "Any match will be replaced with this text." -L.Options_Replacement_CaseSensitive_Name = "case sensitive" -L.Options_Replacement_CaseSensitive_Desc = "Will not replace occurrences if cases differ." +L.Options_Replacement_IgnoreCase_Name = "ignore case" +L.Options_Replacement_IgnoreCase_Desc = "When deactivated matches are case-sensitive.|nThe case for each letter of the matching text is honoured when replaced." L.Options_Replacement_Consolidate_Name = "consolidate consecutive matches" L.Options_Replacement_Consolidate_Desc = "If after the replacement a text sequence is repeated|ndirectly after another, treat them as one occurrence." L.Options_Replacement_Delete_Name = "Delete"