diff --git a/Details.toc b/Details.toc
index 9ceec088..17921508 100644
--- a/Details.toc
+++ b/Details.toc
@@ -1,4 +1,4 @@
-## Interface: 80000
+## Interface: 80100
## Title: Details
## Notes: Computes detailed infos about combats.
## SavedVariables: _detalhes_global
diff --git a/Libs/AceAddon-3.0/AceAddon-3.0.lua b/Libs/AceAddon-3.0/AceAddon-3.0.lua
index b338695f..60215b7b 100644
--- a/Libs/AceAddon-3.0/AceAddon-3.0.lua
+++ b/Libs/AceAddon-3.0/AceAddon-3.0.lua
@@ -28,7 +28,7 @@
-- end
-- @class file
-- @name AceAddon-3.0.lua
--- @release $Id: AceAddon-3.0.lua 1084 2013-04-27 20:14:11Z nevcairiel $
+-- @release $Id: AceAddon-3.0.lua 1184 2018-07-21 14:13:14Z nevcairiel $
local MAJOR, MINOR = "AceAddon-3.0", 12
local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
@@ -62,43 +62,12 @@ local function errorhandler(err)
return geterrorhandler()(err)
end
-local function CreateDispatcher(argCount)
- local code = [[
- local xpcall, eh = ...
- local method, ARGS
- local function call() return method(ARGS) end
-
- local function dispatch(func, ...)
- method = func
- if not method then return end
- ARGS = ...
- return xpcall(call, eh)
- end
-
- return dispatch
- ]]
-
- local ARGS = {}
- for i = 1, argCount do ARGS[i] = "arg"..i end
- code = code:gsub("ARGS", tconcat(ARGS, ", "))
- return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
-end
-
-local Dispatchers = setmetatable({}, {__index=function(self, argCount)
- local dispatcher = CreateDispatcher(argCount)
- rawset(self, argCount, dispatcher)
- return dispatcher
-end})
-Dispatchers[0] = function(func)
- return xpcall(func, errorhandler)
-end
-
local function safecall(func, ...)
-- we check to see if the func is passed is actually a function here and don't error when it isn't
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
-- present execution should continue without hinderance
if type(func) == "function" then
- return Dispatchers[select('#', ...)](func, ...)
+ return xpcall(func, errorhandler, ...)
end
end
diff --git a/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
index eaa0d5d6..3bd4a372 100644
--- a/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
+++ b/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua
@@ -1,5 +1,5 @@
---[[ $Id: CallbackHandler-1.0.lua 1131 2015-06-04 07:29:24Z nevcairiel $ ]]
-local MAJOR, MINOR = "CallbackHandler-1.0", 6
+--[[ $Id: CallbackHandler-1.0.lua 1186 2018-07-21 14:19:18Z nevcairiel $ ]]
+local MAJOR, MINOR = "CallbackHandler-1.0", 7
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
if not CallbackHandler then return end -- No upgrade needed
@@ -22,41 +22,15 @@ local function errorhandler(err)
return geterrorhandler()(err)
end
-local function CreateDispatcher(argCount)
- local code = [[
- local next, xpcall, eh = ...
-
- local method, ARGS
- local function call() method(ARGS) end
-
- local function dispatch(handlers, ...)
- local index
- index, method = next(handlers)
- if not method then return end
- local OLD_ARGS = ARGS
- ARGS = ...
- repeat
- xpcall(call, eh)
- index, method = next(handlers, index)
- until not method
- ARGS = OLD_ARGS
- end
-
- return dispatch
- ]]
-
- local ARGS, OLD_ARGS = {}, {}
- for i = 1, argCount do ARGS[i], OLD_ARGS[i] = "arg"..i, "old_arg"..i end
- code = code:gsub("OLD_ARGS", tconcat(OLD_ARGS, ", ")):gsub("ARGS", tconcat(ARGS, ", "))
- return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(next, xpcall, errorhandler)
+local function Dispatch(handlers, ...)
+ local index, method = next(handlers)
+ if not method then return end
+ repeat
+ xpcall(method, errorhandler, ...)
+ index, method = next(handlers, index)
+ until not method
end
-local Dispatchers = setmetatable({}, {__index=function(self, argCount)
- local dispatcher = CreateDispatcher(argCount)
- rawset(self, argCount, dispatcher)
- return dispatcher
-end})
-
--------------------------------------------------------------------------
-- CallbackHandler:New
--
@@ -87,7 +61,7 @@ function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAll
local oldrecurse = registry.recurse
registry.recurse = oldrecurse + 1
- Dispatchers[select('#', ...) + 1](events[eventname], eventname, ...)
+ Dispatch(events[eventname], eventname, ...)
registry.recurse = oldrecurse
diff --git a/Libs/LibCompress/LibCompress.lua b/Libs/LibCompress/LibCompress.lua
index 63bda273..87ce1e4f 100644
--- a/Libs/LibCompress/LibCompress.lua
+++ b/Libs/LibCompress/LibCompress.lua
@@ -5,12 +5,12 @@
-- Authors: jjsheets and Galmok of European Stormrage (Horde)
-- Email : sheets.jeff@gmail.com and galmok@gmail.com
-- Licence: GPL version 2 (General Public License)
--- Revision: $Revision: 81 $
--- Date: $Date: 2018-02-25 06:31:34 +0000 (Sun, 25 Feb 2018) $
+-- Revision: $Revision: 83 $
+-- Date: $Date: 2018-07-03 14:33:48 +0000 (Tue, 03 Jul 2018) $
----------------------------------------------------------------------------------
-local LibCompress = LibStub:NewLibrary("LibCompress", 90000 + tonumber(("$Revision: 81 $"):match("%d+")))
+local LibCompress = LibStub:NewLibrary("LibCompress", 90000 + tonumber(("$Revision: 83 $"):match("%d+")))
if not LibCompress then return end
@@ -938,13 +938,9 @@ function LibCompress:GetEncodeTable(reservedChars, escapeChars, mapChars)
c = string_sub(encodeBytes, i, i)
if not encode_translate[c] then
-- this loop will update escapeChar and r
- while r < 256 and taken[string_char(r)] do
+ while r >= 256 or taken[string_char(r)] do
r = r + 1
if r > 255 then -- switch to next escapeChar
- if escapeChar == "" then -- we are out of escape chars and we need more!
- return nil, "Out of escape characters"
- end
-
codecTable["decode_search"..tostring(escapeCharIndex)] = escape_for_gsub(escapeChar).."([".. escape_for_gsub(table_concat(decode_search)).."])"
codecTable["decode_translate"..tostring(escapeCharIndex)] = decode_translate
table_insert(decode_func_string, "str = str:gsub(self.decode_search"..tostring(escapeCharIndex)..", self.decode_translate"..tostring(escapeCharIndex)..");")
@@ -952,6 +948,10 @@ function LibCompress:GetEncodeTable(reservedChars, escapeChars, mapChars)
escapeCharIndex = escapeCharIndex + 1
escapeChar = string_sub(escapeChars, escapeCharIndex, escapeCharIndex)
+ if escapeChar == "" then -- we are out of escape chars and we need more!
+ return nil, "Out of escape characters"
+ end
+
r = 0
decode_search = {}
decode_translate = {}
@@ -980,7 +980,6 @@ function LibCompress:GetEncodeTable(reservedChars, escapeChars, mapChars)
encode_func = assert(loadstring("return function(self, str) return str:gsub(self.encode_search, self.encode_translate); end"))()
decode_func = assert(loadstring(decode_func_string))()
-
codecTable.encode_search = encode_search
codecTable.encode_translate = encode_translate
codecTable.Encode = encode_func
@@ -1253,4 +1252,4 @@ end
function LibCompress:fcs32final(uFcs32)
return bit_bnot(uFcs32)
-end
+end
\ No newline at end of file
diff --git a/Libs/LibCompress/LibCompress.toc b/Libs/LibCompress/LibCompress.toc
index 08c48d22..ca237574 100644
--- a/Libs/LibCompress/LibCompress.toc
+++ b/Libs/LibCompress/LibCompress.toc
@@ -1,14 +1,14 @@
-## Interface: 70300
+## Interface: 70300
## Title: Lib: Compress
## Notes: Compression and Decompression library
## Author: Galmok at Stormrage-EU (Horde) and JJSheets
-## Version: r82-release
+## Version: r84-release
## X-Website: http://www.wowace.com/addons/libcompress/
## X-Category: Library
## X-eMail: galmok AT gmail DOT com, sheets DOT jeff AT gmail DOT com
-## X-License: LGPL v2.1
+## X-License: GPL v2
## LoadOnDemand: 1
LibStub\LibStub.lua
-lib.xml
+lib.xml
\ No newline at end of file
diff --git a/Libs/LibCompress/lib.xml b/Libs/LibCompress/lib.xml
index d35c6a9f..c85b2134 100644
--- a/Libs/LibCompress/lib.xml
+++ b/Libs/LibCompress/lib.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua b/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua
index 00925ec0..b236b6ef 100644
--- a/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua
+++ b/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua
@@ -6,19 +6,21 @@
--
local DBICON10 = "LibDBIcon-1.0"
-local DBICON10_MINOR = 36 -- Bump on changes
+local DBICON10_MINOR = 43 -- Bump on changes
if not LibStub then error(DBICON10 .. " requires LibStub.") end
local ldb = LibStub("LibDataBroker-1.1", true)
if not ldb then error(DBICON10 .. " requires LibDataBroker-1.1.") end
local lib = LibStub:NewLibrary(DBICON10, DBICON10_MINOR)
if not lib then return end
-lib.disabled = lib.disabled or nil
lib.objects = lib.objects or {}
lib.callbackRegistered = lib.callbackRegistered or nil
lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
lib.notCreated = lib.notCreated or {}
+lib.radius = lib.radius or 5
lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", "LibDBIconTooltip", UIParent, "GameTooltipTemplate")
+local next, Minimap = next, Minimap
+local isDraggingButton = false
function lib:IconCallback(event, name, key, value)
if lib.objects[name] then
@@ -56,7 +58,15 @@ local function getAnchors(frame)
end
local function onEnter(self)
- if self.isMoving then return end
+ if isDraggingButton then return end
+
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Stop()
+ button:SetAlpha(1)
+ end
+ end
+
local obj = self.dataObject
if obj.OnTooltipShow then
lib.tooltip:SetOwner(self, "ANCHOR_NONE")
@@ -69,14 +79,25 @@ local function onEnter(self)
end
local function onLeave(self)
- local obj = self.dataObject
lib.tooltip:Hide()
- if obj.OnLeave then obj.OnLeave(self) end
+
+ if not isDraggingButton then
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Play()
+ end
+ end
+ end
+
+ local obj = self.dataObject
+ if obj.OnLeave then
+ obj.OnLeave(self)
+ end
end
--------------------------------------------------------------------------------
-local onClick, onMouseUp, onMouseDown, onDragStart, onDragStop, updatePosition
+local onDragStart, updatePosition
do
local minimapShapes = {
@@ -96,40 +117,60 @@ do
["TRICORNER-BOTTOMRIGHT"] = {true, true, true, false},
}
- function updatePosition(button)
- local angle = math.rad(button.db and button.db.minimapPos or button.minimapPos or 225)
- local x, y, q = math.cos(angle), math.sin(angle), 1
+ local rad, cos, sin, sqrt, max, min = math.rad, math.cos, math.sin, math.sqrt, math.max, math.min
+ function updatePosition(button, position)
+ local angle = rad(position or 225)
+ local x, y, q = cos(angle), sin(angle), 1
if x < 0 then q = q + 1 end
if y > 0 then q = q + 2 end
local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
local quadTable = minimapShapes[minimapShape]
+ local w = (Minimap:GetWidth() / 2) + lib.radius
+ local h = (Minimap:GetHeight() / 2) + lib.radius
if quadTable[q] then
- x, y = x*80, y*80
+ x, y = x*w, y*h
else
- local diagRadius = 103.13708498985 --math.sqrt(2*(80)^2)-10
- x = math.max(-80, math.min(x*diagRadius, 80))
- y = math.max(-80, math.min(y*diagRadius, 80))
+ local diagRadiusW = sqrt(2*(w)^2)-10
+ local diagRadiusH = sqrt(2*(h)^2)-10
+ x = max(-w, min(x*diagRadiusW, w))
+ y = max(-h, min(y*diagRadiusH, h))
end
button:SetPoint("CENTER", Minimap, "CENTER", x, y)
end
end
-function onClick(self, b) if self.dataObject.OnClick then self.dataObject.OnClick(self, b) end end
-function onMouseDown(self) self.isMouseDown = true; self.icon:UpdateCoord() end
-function onMouseUp(self) self.isMouseDown = false; self.icon:UpdateCoord() end
+local function onClick(self, b)
+ if self.dataObject.OnClick then
+ self.dataObject.OnClick(self, b)
+ end
+end
+
+local function onMouseDown(self)
+ self.isMouseDown = true
+ self.icon:UpdateCoord()
+end
+
+local function onMouseUp(self)
+ self.isMouseDown = false
+ self.icon:UpdateCoord()
+end
do
+ local deg, atan2 = math.deg, math.atan2
local function onUpdate(self)
local mx, my = Minimap:GetCenter()
local px, py = GetCursorPosition()
local scale = Minimap:GetEffectiveScale()
px, py = px / scale, py / scale
+ local pos = 225
if self.db then
- self.db.minimapPos = math.deg(math.atan2(py - my, px - mx)) % 360
+ pos = deg(atan2(py - my, px - mx)) % 360
+ self.db.minimapPos = pos
else
- self.minimapPos = math.deg(math.atan2(py - my, px - mx)) % 360
+ pos = deg(atan2(py - my, px - mx)) % 360
+ self.minimapPos = pos
end
- updatePosition(self)
+ updatePosition(self, pos)
end
function onDragStart(self)
@@ -137,17 +178,28 @@ do
self.isMouseDown = true
self.icon:UpdateCoord()
self:SetScript("OnUpdate", onUpdate)
- self.isMoving = true
+ isDraggingButton = true
lib.tooltip:Hide()
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Stop()
+ button:SetAlpha(1)
+ end
+ end
end
end
-function onDragStop(self)
+local function onDragStop(self)
self:SetScript("OnUpdate", nil)
self.isMouseDown = false
self.icon:UpdateCoord()
self:UnlockHighlight()
- self.isMoving = nil
+ isDraggingButton = false
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Play()
+ end
+ end
end
local defaultCoords = {0, 1, 0, 1}
@@ -202,12 +254,24 @@ local function createButton(name, object, db)
button:SetScript("OnMouseDown", onMouseDown)
button:SetScript("OnMouseUp", onMouseUp)
+ button.fadeOut = button:CreateAnimationGroup()
+ local animOut = button.fadeOut:CreateAnimation("Alpha")
+ animOut:SetOrder(1)
+ animOut:SetDuration(0.2)
+ animOut:SetFromAlpha(1)
+ animOut:SetToAlpha(0)
+ animOut:SetStartDelay(1)
+ button.fadeOut:SetToFinalAlpha(true)
+
lib.objects[name] = button
if lib.loggedIn then
- updatePosition(button)
- if not db or not db.hide then button:Show()
- else button:Hide() end
+ updatePosition(button, db and db.minimapPos)
+ if not db or not db.hide then
+ button:Show()
+ else
+ button:Hide()
+ end
end
lib.callbacks:Fire("LibDBIcon_IconCreated", button, name) -- Fire 'Icon Created' callback
end
@@ -221,20 +285,21 @@ local function check(name)
end
end
-lib.loggedIn = lib.loggedIn or false
-- Wait a bit with the initial positioning to let any GetMinimapShape addons
-- load up.
if not lib.loggedIn then
local f = CreateFrame("Frame")
- f:SetScript("OnEvent", function()
- for _, object in pairs(lib.objects) do
- updatePosition(object)
- if not lib.disabled and (not object.db or not object.db.hide) then object:Show()
- else object:Hide() end
+ f:SetScript("OnEvent", function(f)
+ for _, button in next, lib.objects do
+ updatePosition(button, button.db and button.db.minimapPos)
+ if not button.db or not button.db.hide then
+ button:Show()
+ else
+ button:Hide()
+ end
end
lib.loggedIn = true
f:SetScript("OnEvent", nil)
- f = nil
end)
f:RegisterEvent("PLAYER_LOGIN")
end
@@ -245,8 +310,8 @@ end
function lib:Register(name, object, db)
if not object.icon then error("Can't register LDB objects without icons set!") end
- if lib.objects[name] or lib.notCreated[name] then error("Already registered, nubcake.") end
- if not lib.disabled and (not db or not db.hide) then
+ if lib.objects[name] or lib.notCreated[name] then error(DBICON10.. ": Object '".. name .."' is already registered.") end
+ if not db or not db.hide then
createButton(name, object, db)
else
lib.notCreated[name] = {object, db}
@@ -260,7 +325,9 @@ function lib:Lock(name)
lib.objects[name]:SetScript("OnDragStop", nil)
end
local db = getDatabase(name)
- if db then db.lock = true end
+ if db then
+ db.lock = true
+ end
end
function lib:Unlock(name)
@@ -270,28 +337,36 @@ function lib:Unlock(name)
lib.objects[name]:SetScript("OnDragStop", onDragStop)
end
local db = getDatabase(name)
- if db then db.lock = nil end
+ if db then
+ db.lock = nil
+ end
end
function lib:Hide(name)
if not lib.objects[name] then return end
lib.objects[name]:Hide()
end
+
function lib:Show(name)
- if lib.disabled then return end
check(name)
- lib.objects[name]:Show()
- updatePosition(lib.objects[name])
+ local button = lib.objects[name]
+ if button then
+ button:Show()
+ updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
+ end
end
+
function lib:IsRegistered(name)
return (lib.objects[name] or lib.notCreated[name]) and true or false
end
+
function lib:Refresh(name, db)
- if lib.disabled then return end
check(name)
local button = lib.objects[name]
- if db then button.db = db end
- updatePosition(button)
+ if db then
+ button.db = db
+ end
+ updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
if not button.db or not button.db.hide then
button:Show()
else
@@ -305,30 +380,91 @@ function lib:Refresh(name, db)
button:SetScript("OnDragStop", nil)
end
end
+
function lib:GetMinimapButton(name)
return lib.objects[name]
end
-function lib:EnableLibrary()
- lib.disabled = nil
- for name, object in pairs(lib.objects) do
- if not object.db or not object.db.hide then
- object:Show()
- updatePosition(object)
+do
+ local function OnMinimapEnter()
+ if isDraggingButton then return end
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Stop()
+ button:SetAlpha(1)
+ end
end
end
- for name, data in pairs(lib.notCreated) do
- if not data.db or not data.db.hide then
- createButton(name, data[1], data[2])
- lib.notCreated[name] = nil
+ local function OnMinimapLeave()
+ if isDraggingButton then return end
+ for _, button in next, lib.objects do
+ if button.showOnMouseover then
+ button.fadeOut:Play()
+ end
+ end
+ end
+ Minimap:HookScript("OnEnter", OnMinimapEnter)
+ Minimap:HookScript("OnLeave", OnMinimapLeave)
+
+ function lib:ShowOnEnter(name, value)
+ local button = lib.objects[name]
+ if button then
+ if value then
+ button.showOnMouseover = true
+ button.fadeOut:Stop()
+ button:SetAlpha(0)
+ else
+ button.showOnMouseover = false
+ button.fadeOut:Stop()
+ button:SetAlpha(1)
+ end
end
end
end
-function lib:DisableLibrary()
- lib.disabled = true
- for name, object in pairs(lib.objects) do
- object:Hide()
+function lib:GetButtonList()
+ local t = {}
+ for name in next, lib.objects do
+ t[#t+1] = name
+ end
+ return t
+end
+
+function lib:SetButtonRadius(radius)
+ if type(radius) == "number" then
+ lib.radius = radius
+ for _, button in next, lib.objects do
+ updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
+ end
end
end
+function lib:SetButtonToPosition(button, position)
+ updatePosition(lib.objects[button] or button, position)
+end
+
+-- Upgrade!
+for name, button in next, lib.objects do
+ local db = getDatabase(name)
+ if not db or not db.lock then
+ button:SetScript("OnDragStart", onDragStart)
+ button:SetScript("OnDragStop", onDragStop)
+ end
+ button:SetScript("OnEnter", onEnter)
+ button:SetScript("OnLeave", onLeave)
+ button:SetScript("OnClick", onClick)
+ button:SetScript("OnMouseDown", onMouseDown)
+ button:SetScript("OnMouseUp", onMouseUp)
+
+ if not button.fadeOut then -- Upgrade to 39
+ button.fadeOut = button:CreateAnimationGroup()
+ local animOut = button.fadeOut:CreateAnimation("Alpha")
+ animOut:SetOrder(1)
+ animOut:SetDuration(0.2)
+ animOut:SetFromAlpha(1)
+ animOut:SetToAlpha(0)
+ animOut:SetStartDelay(1)
+ button.fadeOut:SetToFinalAlpha(true)
+ end
+end
+lib:SetButtonRadius(lib.radius) -- Upgrade to 40
diff --git a/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua b/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua
index 4a31cfeb..1e1e25f0 100644
--- a/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua
+++ b/Libs/LibSharedMedia-3.0/LibSharedMedia-3.0.lua
@@ -1,6 +1,6 @@
--[[
Name: LibSharedMedia-3.0
-Revision: $Revision: 91 $
+Revision: $Revision: 106 $
Author: Elkano (elkano@gmx.de)
Inspired By: SurfaceLib by Haste/Otravi (troeks@gmail.com)
Website: http://www.wowace.com/projects/libsharedmedia-3-0/
@@ -20,8 +20,6 @@ local pairs = _G.pairs
local type = _G.type
local band = _G.bit.band
-
-local table_insert = _G.table.insert
local table_sort = _G.table.sort
local locale = GetLocale()
diff --git a/Libs/LibSharedMedia-3.0/lib.xml b/Libs/LibSharedMedia-3.0/lib.xml
index 7313228b..5b4a5c87 100644
--- a/Libs/LibSharedMedia-3.0/lib.xml
+++ b/Libs/LibSharedMedia-3.0/lib.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/indent.lua b/indent.lua
index 4eedbf38..148b661f 100644
--- a/indent.lua
+++ b/indent.lua
@@ -1,5 +1,5 @@
-- For All Indents And Purposes
-local revision = 17
+local revision = 21
-- Maintainer: kristofer.karlsson@gmail.com
-- For All Indents And Purposes -
@@ -22,1304 +22,1293 @@ local revision = 17
-- Read through this code for further usage help.
-- (The documentation IS the code)
+-- luacheck: globals IndentationLib
+
if not IndentationLib then
- IndentationLib = {}
+ IndentationLib = {}
end
if not IndentationLib.revision or revision > IndentationLib.revision then
- local lib = IndentationLib
- lib.revision = revision
-
- local stringlen = string.len
- local stringformat = string.format
- local stringfind = string.find
- local stringsub = string.sub
- local stringbyte = string.byte
- local stringchar = string.char
- local stringrep = string.rep
- local stringgsub = string.gsub
-
- local workingTable = {}
- local workingTable2 = {}
- local function tableclear(t)
- for k in next,t do
- t[k] = nil
- end
- end
-
- local function stringinsert(s, pos, insertStr)
- return stringsub(s, 1, pos) .. insertStr .. stringsub(s, pos + 1)
- end
- lib.stringinsert = stringinsert
-
- local function stringdelete(s, pos1, pos2)
- return stringsub(s, 1, pos1 - 1) .. stringsub(s, pos2 + 1)
- end
- lib.stringdelete = stringdelete
-
- -- token types
- local tokens = {}
- lib.tokens = tokens
-
- tokens.TOKEN_UNKNOWN = 0
- tokens.TOKEN_NUMBER = 1
- tokens.TOKEN_LINEBREAK = 2
- tokens.TOKEN_WHITESPACE = 3
- tokens.TOKEN_IDENTIFIER = 4
- tokens.TOKEN_ASSIGNMENT = 5
- tokens.TOKEN_EQUALITY = 6
- tokens.TOKEN_MINUS = 7
- tokens.TOKEN_COMMENT_SHORT = 8
- tokens.TOKEN_COMMENT_LONG = 9
- tokens.TOKEN_STRING = 10
- tokens.TOKEN_LEFTBRACKET = 11
- tokens.TOKEN_PERIOD = 12
- tokens.TOKEN_DOUBLEPERIOD = 13
- tokens.TOKEN_TRIPLEPERIOD = 14
- tokens.TOKEN_LTE = 15
- tokens.TOKEN_LT = 16
- tokens.TOKEN_GTE = 17
- tokens.TOKEN_GT = 18
- tokens.TOKEN_NOTEQUAL = 19
- tokens.TOKEN_COMMA = 20
- tokens.TOKEN_SEMICOLON = 21
- tokens.TOKEN_COLON = 22
- tokens.TOKEN_LEFTPAREN = 23
- tokens.TOKEN_RIGHTPAREN = 24
- tokens.TOKEN_PLUS = 25
- tokens.TOKEN_SLASH = 27
- tokens.TOKEN_LEFTWING = 28
- tokens.TOKEN_RIGHTWING = 29
- tokens.TOKEN_CIRCUMFLEX = 30
- tokens.TOKEN_ASTERISK = 31
- tokens.TOKEN_RIGHTBRACKET = 32
- tokens.TOKEN_KEYWORD = 33
- tokens.TOKEN_SPECIAL = 34
- tokens.TOKEN_VERTICAL = 35
- tokens.TOKEN_TILDE = 36
-
- -- WoW specific tokens
- tokens.TOKEN_COLORCODE_START = 37
- tokens.TOKEN_COLORCODE_STOP = 38
-
--- new as of lua 5.1
- tokens.TOKEN_HASH = 39
- tokens.TOKEN_PERCENT = 40
-
-
- -- ascii codes
- local bytes = {}
- lib.bytes = bytes
- bytes.BYTE_LINEBREAK_UNIX = stringbyte("\n")
- bytes.BYTE_LINEBREAK_MAC = stringbyte("\r")
- bytes.BYTE_SINGLE_QUOTE = stringbyte("'")
- bytes.BYTE_DOUBLE_QUOTE = stringbyte('"')
- bytes.BYTE_0 = stringbyte("0")
- bytes.BYTE_9 = stringbyte("9")
- bytes.BYTE_PERIOD = stringbyte(".")
- bytes.BYTE_SPACE = stringbyte(" ")
- bytes.BYTE_TAB = stringbyte("\t")
- bytes.BYTE_E = stringbyte("E")
- bytes.BYTE_e = stringbyte("e")
- bytes.BYTE_MINUS = stringbyte("-")
- bytes.BYTE_EQUALS = stringbyte("=")
- bytes.BYTE_LEFTBRACKET = stringbyte("[")
- bytes.BYTE_RIGHTBRACKET = stringbyte("]")
- bytes.BYTE_BACKSLASH = stringbyte("\\")
- bytes.BYTE_COMMA = stringbyte(",")
- bytes.BYTE_SEMICOLON = stringbyte(";")
- bytes.BYTE_COLON = stringbyte(":")
- bytes.BYTE_LEFTPAREN = stringbyte("(")
- bytes.BYTE_RIGHTPAREN = stringbyte(")")
- bytes.BYTE_TILDE = stringbyte("~")
- bytes.BYTE_PLUS = stringbyte("+")
- bytes.BYTE_SLASH = stringbyte("/")
- bytes.BYTE_LEFTWING = stringbyte("{")
- bytes.BYTE_RIGHTWING = stringbyte("}")
- bytes.BYTE_CIRCUMFLEX = stringbyte("^")
- bytes.BYTE_ASTERISK = stringbyte("*")
- bytes.BYTE_LESSTHAN = stringbyte("<")
- bytes.BYTE_GREATERTHAN = stringbyte(">")
- -- WoW specific chars
- bytes.BYTE_VERTICAL = stringbyte("|")
- bytes.BYTE_r = stringbyte("r")
- bytes.BYTE_c = stringbyte("c")
-
--- new as of lua 5.1
- bytes.BYTE_HASH = stringbyte("#")
- bytes.BYTE_PERCENT = stringbyte("%")
-
-
- local linebreakCharacters = {}
- lib.linebreakCharacters = linebreakCharacters
- linebreakCharacters[bytes.BYTE_LINEBREAK_UNIX] = 1
- linebreakCharacters[bytes.BYTE_LINEBREAK_MAC] = 1
-
- local whitespaceCharacters = {}
- lib.whitespaceCharacters = whitespaceCharacters
- whitespaceCharacters[bytes.BYTE_SPACE] = 1
- whitespaceCharacters[bytes.BYTE_TAB] = 1
-
- local specialCharacters = {}
- lib.specialCharacters = specialCharacters
- specialCharacters[bytes.BYTE_PERIOD] = -1
- specialCharacters[bytes.BYTE_LESSTHAN] = -1
- specialCharacters[bytes.BYTE_GREATERTHAN] = -1
- specialCharacters[bytes.BYTE_LEFTBRACKET] = -1
- specialCharacters[bytes.BYTE_EQUALS] = -1
- specialCharacters[bytes.BYTE_MINUS] = -1
- specialCharacters[bytes.BYTE_SINGLE_QUOTE] = -1
- specialCharacters[bytes.BYTE_DOUBLE_QUOTE] = -1
- specialCharacters[bytes.BYTE_TILDE] = -1
- specialCharacters[bytes.BYTE_RIGHTBRACKET] = tokens.TOKEN_RIGHTBRACKET
- specialCharacters[bytes.BYTE_COMMA] = tokens.TOKEN_COMMA
- specialCharacters[bytes.BYTE_COLON] = tokens.TOKEN_COLON
- specialCharacters[bytes.BYTE_SEMICOLON] = tokens.TOKEN_SEMICOLON
- specialCharacters[bytes.BYTE_LEFTPAREN] = tokens.TOKEN_LEFTPAREN
- specialCharacters[bytes.BYTE_RIGHTPAREN] = tokens.TOKEN_RIGHTPAREN
- specialCharacters[bytes.BYTE_PLUS] = tokens.TOKEN_PLUS
- specialCharacters[bytes.BYTE_SLASH] = tokens.TOKEN_SLASH
- specialCharacters[bytes.BYTE_LEFTWING] = tokens.TOKEN_LEFTWING
- specialCharacters[bytes.BYTE_RIGHTWING] = tokens.TOKEN_RIGHTWING
- specialCharacters[bytes.BYTE_CIRCUMFLEX] = tokens.TOKEN_CIRCUMFLEX
- specialCharacters[bytes.BYTE_ASTERISK] = tokens.TOKEN_ASTERISK
- -- WoW specific
- specialCharacters[bytes.BYTE_VERTICAL] = -1
--- new as of lua 5.1
- specialCharacters[bytes.BYTE_HASH] = tokens.TOKEN_HASH
- specialCharacters[bytes.BYTE_PERCENT] = tokens.TOKEN_PERCENT
-
- local function nextNumberExponentPartInt(text, pos)
- while true do
- local byte = stringbyte(text, pos)
- if not byte then
- return tokens.TOKEN_NUMBER, pos
- end
-
- if byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
- pos = pos + 1
- else
- return tokens.TOKEN_NUMBER, pos
- end
- end
- end
-
- local function nextNumberExponentPart(text, pos)
- local byte = stringbyte(text, pos)
- if not byte then
- return tokens.TOKEN_NUMBER, pos
- end
-
- if byte == bytes.BYTE_MINUS then
- -- handle this case: a = 1.2e-- some comment
- -- i decide to let 1.2e be parsed as a a number
- byte = stringbyte(text, pos + 1)
- if byte == bytes.BYTE_MINUS then
- return tokens.TOKEN_NUMBER, pos
- end
- return nextNumberExponentPartInt(text, pos + 1)
- end
-
- return nextNumberExponentPartInt(text, pos)
- end
-
- local function nextNumberFractionPart(text, pos)
- while true do
- local byte = stringbyte(text, pos)
- if not byte then
- return tokens.TOKEN_NUMBER, pos
- end
-
- if byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
- pos = pos + 1
- elseif byte == bytes.BYTE_E or byte == bytes.BYTE_e then
- return nextNumberExponentPart(text, pos + 1)
- else
- return tokens.TOKEN_NUMBER, pos
- end
- end
- end
-
- local function nextNumberIntPart(text, pos)
- while true do
- local byte = stringbyte(text, pos)
- if not byte then
- return tokens.TOKEN_NUMBER, pos
- end
-
- if byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
- pos = pos + 1
- elseif byte == bytes.BYTE_PERIOD then
- return nextNumberFractionPart(text, pos + 1)
- elseif byte == bytes.BYTE_E or byte == bytes.BYTE_e then
- return nextNumberExponentPart(text, pos + 1)
- else
- return tokens.TOKEN_NUMBER, pos
- end
- end
- end
-
- local function nextIdentifier(text, pos)
- while true do
- local byte = stringbyte(text, pos)
-
- if not byte or
- linebreakCharacters[byte] or
- whitespaceCharacters[byte] or
- specialCharacters[byte] then
- return tokens.TOKEN_IDENTIFIER, pos
- end
- pos = pos + 1
- end
- end
-
- -- returns false or: true, nextPos, equalsCount
- local function isBracketStringNext(text, pos)
- local byte = stringbyte(text, pos)
- if byte == bytes.BYTE_LEFTBRACKET then
- local pos2 = pos + 1
- byte = stringbyte(text, pos2)
- while byte == bytes.BYTE_EQUALS do
- pos2 = pos2 + 1
- byte = stringbyte(text, pos2)
- end
- if byte == bytes.BYTE_LEFTBRACKET then
- return true, pos2 + 1, (pos2 - 1) - pos
- else
- return false
- end
- else
- return false
- end
- end
-
-
- -- Already parsed the [==[ part when get here
- local function nextBracketString(text, pos, equalsCount)
- local state = 0
- while true do
- local byte = stringbyte(text, pos)
- if not byte then
- return tokens.TOKEN_STRING, pos
- end
-
- if byte == bytes.BYTE_RIGHTBRACKET then
- if state == 0 then
- state = 1
- elseif state == equalsCount + 1 then
- return tokens.TOKEN_STRING, pos + 1
- else
- state = 0
- end
- elseif byte == bytes.BYTE_EQUALS then
- if state > 0 then
- state = state + 1
- end
- else
- state = 0
- end
- pos = pos + 1
- end
- end
-
- local function nextComment(text, pos)
- -- When we get here we have already parsed the "--"
- -- Check for long comment
- local isBracketString, nextPos, equalsCount = isBracketStringNext(text, pos)
- if isBracketString then
- local tokenType, nextPos2 = nextBracketString(text, nextPos, equalsCount)
- return tokens.TOKEN_COMMENT_LONG, nextPos2
- end
-
- local byte = stringbyte(text, pos)
-
- -- Short comment, find the first linebreak
- while true do
- byte = stringbyte(text, pos)
- if not byte then
- return tokens.TOKEN_COMMENT_SHORT, pos
- end
- if linebreakCharacters[byte] then
- return tokens.TOKEN_COMMENT_SHORT, pos
- end
- pos = pos + 1
- end
- end
-
- local function nextString(text, pos, character)
- local even = true
- while true do
- local byte = stringbyte(text, pos)
- if not byte then
- return tokens.TOKEN_STRING, pos
- end
-
- if byte == character then
- if even then
- return tokens.TOKEN_STRING, pos + 1
- end
- end
- if byte == bytes.BYTE_BACKSLASH then
- even = not even
- else
- even = true
- end
-
- pos = pos + 1
- end
- end
-
- -- INPUT
- -- 1: text: text to search in
- -- 2: tokenPos: where to start searching
- -- OUTPUT
- -- 1: token type
- -- 2: position after the last character of the token
- function nextToken(text, pos)
- local byte = stringbyte(text, pos)
- if not byte then
- return nil
- end
-
- if linebreakCharacters[byte] then
- return tokens.TOKEN_LINEBREAK, pos + 1
- end
-
- if whitespaceCharacters[byte] then
- while true do
- pos = pos + 1
- byte = stringbyte(text, pos)
- if not byte or not whitespaceCharacters[byte] then
- return tokens.TOKEN_WHITESPACE, pos
- end
- end
- end
-
- local token = specialCharacters[byte]
- if token then
- if token ~= -1 then
- return token, pos + 1
- end
-
- -- WoW specific (for color codes)
- if byte == bytes.BYTE_VERTICAL then
- byte = stringbyte(text, pos + 1)
- if byte == bytes.BYTE_VERTICAL then
- return tokens.TOKEN_VERTICAL, pos + 2
- end
- if byte == bytes.BYTE_c then
- return tokens.TOKEN_COLORCODE_START, pos + 10
- end
- if byte == bytes.BYTE_r then
- return tokens.TOKEN_COLORCODE_STOP, pos + 2
- end
- return tokens.TOKEN_UNKNOWN, pos + 1
- end
-
- if byte == bytes.BYTE_MINUS then
- byte = stringbyte(text, pos + 1)
- if byte == bytes.BYTE_MINUS then
- return nextComment(text, pos + 2)
- end
- return tokens.TOKEN_MINUS, pos + 1
- end
-
- if byte == bytes.BYTE_SINGLE_QUOTE then
- return nextString(text, pos + 1, bytes.BYTE_SINGLE_QUOTE)
- end
-
- if byte == bytes.BYTE_DOUBLE_QUOTE then
- return nextString(text, pos + 1, bytes.BYTE_DOUBLE_QUOTE)
- end
-
- if byte == bytes.BYTE_LEFTBRACKET then
- local isBracketString, nextPos, equalsCount = isBracketStringNext(text, pos)
- if isBracketString then
- return nextBracketString(text, nextPos, equalsCount)
- else
- return tokens.TOKEN_LEFTBRACKET, pos + 1
- end
- end
-
- if byte == bytes.BYTE_EQUALS then
- byte = stringbyte(text, pos + 1)
- if not byte then
- return tokens.TOKEN_ASSIGNMENT, pos + 1
- end
- if byte == bytes.BYTE_EQUALS then
- return tokens.TOKEN_EQUALITY, pos + 2
- end
- return tokens.TOKEN_ASSIGNMENT, pos + 1
- end
-
- if byte == bytes.BYTE_PERIOD then
- byte = stringbyte(text, pos + 1)
- if not byte then
- return tokens.TOKEN_PERIOD, pos + 1
- end
- if byte == bytes.BYTE_PERIOD then
- byte = stringbyte(text, pos + 2)
- if byte == bytes.BYTE_PERIOD then
- return tokens.TOKEN_TRIPLEPERIOD, pos + 3
- end
- return tokens.TOKEN_DOUBLEPERIOD, pos + 2
- elseif byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
- return nextNumberFractionPart(text, pos + 2)
- end
- return tokens.TOKEN_PERIOD, pos + 1
- end
-
- if byte == bytes.BYTE_LESSTHAN then
- byte = stringbyte(text, pos + 1)
- if byte == bytes.BYTE_EQUALS then
- return tokens.TOKEN_LTE, pos + 2
- end
- return tokens.TOKEN_LT, pos + 1
- end
-
- if byte == bytes.BYTE_GREATERTHAN then
- byte = stringbyte(text, pos + 1)
- if byte == bytes.BYTE_EQUALS then
- return tokens.TOKEN_GTE, pos + 2
- end
- return tokens.TOKEN_GT, pos + 1
- end
-
- if byte == bytes.BYTE_TILDE then
- byte = stringbyte(text, pos + 1)
- if byte == bytes.BYTE_EQUALS then
- return tokens.TOKEN_NOTEQUAL, pos + 2
- end
- return tokens.TOKEN_TILDE, pos + 1
- end
-
- return tokens.TOKEN_UNKNOWN, pos + 1
- elseif byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
- return nextNumberIntPart(text, pos + 1)
- else
- return nextIdentifier(text, pos + 1)
- end
- end
-
- -- Cool stuff begins here! (indentation and highlighting)
-
- local noIndentEffect = {0, 0}
- local indentLeft = {-1, 0}
- local indentRight = {0, 1}
- local indentBoth = {-1, 1}
-
- local keywords = {}
- lib.keywords = keywords
- keywords["and"] = noIndentEffect
- keywords["break"] = noIndentEffect
- keywords["false"] = noIndentEffect
- keywords["for"] = noIndentEffect
- keywords["if"] = noIndentEffect
- keywords["in"] = noIndentEffect
- keywords["local"] = noIndentEffect
- keywords["nil"] = noIndentEffect
- keywords["not"] = noIndentEffect
- keywords["or"] = noIndentEffect
- keywords["return"] = noIndentEffect
- keywords["true"] = noIndentEffect
- keywords["while"] = noIndentEffect
-
- keywords["until"] = indentLeft
- keywords["elseif"] = indentLeft
- keywords["end"] = indentLeft
-
- keywords["do"] = indentRight
- keywords["then"] = indentRight
- keywords["repeat"] = indentRight
- keywords["function"] = indentRight
-
- keywords["else"] = indentBoth
-
- tokenIndentation = {}
- lib.tokenIndentation = tokenIndentation
- tokenIndentation[tokens.TOKEN_LEFTPAREN] = indentRight
- tokenIndentation[tokens.TOKEN_LEFTBRACKET] = indentRight
- tokenIndentation[tokens.TOKEN_LEFTWING] = indentRight
-
- tokenIndentation[tokens.TOKEN_RIGHTPAREN] = indentLeft
- tokenIndentation[tokens.TOKEN_RIGHTBRACKET] = indentLeft
- tokenIndentation[tokens.TOKEN_RIGHTWING] = indentLeft
-
- local function fillWithTabs(n)
- return stringrep("\t", n)
- end
-
- local function fillWithSpaces(a, b)
- return stringrep(" ", a*b)
- end
-
- function lib.colorCodeCode(code, colorTable, caretPosition)
- local stopColor = colorTable and colorTable[0]
- if not stopColor then
- return code, caretPosition
- end
-
- local stopColorLen = stringlen(stopColor)
-
- tableclear(workingTable)
- local tsize = 0
- local totalLen = 0
-
- local numLines = 0
- local newCaretPosition
- local prevTokenWasColored = false
- local prevTokenWidth = 0
-
- local pos = 1
- local level = 0
-
- while true do
- if caretPosition and not newCaretPosition and pos >= caretPosition then
- if pos == caretPosition then
- newCaretPosition = totalLen
- else
- newCaretPosition = totalLen
- local diff = pos - caretPosition
- if diff > prevTokenWidth then
- diff = prevTokenWidth
- end
- if prevTokenWasColored then
- diff = diff + stopColorLen
- end
- newCaretPosition = newCaretPosition - diff
- end
- end
-
- prevTokenWasColored = false
- prevTokenWidth = 0
-
- local tokenType, nextPos = nextToken(code, pos)
-
- if not tokenType then
- break
- end
-
- if tokenType == tokens.TOKEN_COLORCODE_START or tokenType == tokens.TOKEN_COLORCODE_STOP or tokenType == tokens.TOKEN_UNKNOWN then
- -- ignore color codes
-
- elseif tokenType == tokens.TOKEN_LINEBREAK or tokenType == tokens.TOKEN_WHITESPACE then
- if tokenType == tokens.TOKEN_LINEBREAK then
- numLines = numLines + 1
- end
- local str = stringsub(code, pos, nextPos - 1)
- prevTokenWidth = nextPos - pos
-
- tsize = tsize + 1
- workingTable[tsize] = str
- totalLen = totalLen + stringlen(str)
- else
- local str = stringsub(code, pos, nextPos - 1)
-
- prevTokenWidth = nextPos - pos
-
- -- Add coloring
- if keywords[str] then
- tokenType = tokens.TOKEN_KEYWORD
- end
-
- local color
- if stopColor then
- color = colorTable[str]
- if not color then
- color = colorTable[tokenType]
- if not color then
- if tokenType == tokens.TOKEN_IDENTIFIER then
- color = colorTable[tokens.TOKEN_IDENTIFIER]
- else
- color = colorTable[tokens.TOKEN_SPECIAL]
- end
- end
- end
- end
-
- if color then
- tsize = tsize + 1
- workingTable[tsize] = color
- tsize = tsize + 1
- workingTable[tsize] = str
- tsize = tsize + 1
- workingTable[tsize] = stopColor
-
- totalLen = totalLen + stringlen(color) + (nextPos - pos) + stopColorLen
- prevTokenWasColored = true
- else
- tsize = tsize + 1
- workingTable[tsize] = str
-
- totalLen = totalLen + stringlen(str)
- end
- end
-
- pos = nextPos
- end
- return table.concat(workingTable), newCaretPosition, numLines
- end
-
- function lib.indentCode(code, tabWidth, colorTable, caretPosition)
- local fillFunction
- if tabWidth == nil then
- tabWidth = defaultTabWidth
- end
- if tabWidth then
- fillFunction = fillWithSpaces
- else
- fillFunction = fillWithTabs
- end
-
- tableclear(workingTable)
- local tsize = 0
- local totalLen = 0
-
- tableclear(workingTable2)
- local tsize2 = 0
- local totalLen2 = 0
-
-
- local stopColor = colorTable and colorTable[0]
- local stopColorLen = not stopColor or stringlen(stopColor)
-
- local newCaretPosition
- local newCaretPositionFinalized = false
- local prevTokenWasColored = false
- local prevTokenWidth = 0
-
- local pos = 1
- local level = 0
-
- local hitNonWhitespace = false
- local hitIndentRight = false
- local preIndent = 0
- local postIndent = 0
- while true do
- if caretPosition and not newCaretPosition and pos >= caretPosition then
- if pos == caretPosition then
- newCaretPosition = totalLen + totalLen2
- else
- newCaretPosition = totalLen + totalLen2
- local diff = pos - caretPosition
- if diff > prevTokenWidth then
- diff = prevTokenWidth
- end
- if prevTokenWasColored then
- diff = diff + stopColorLen
- end
- newCaretPosition = newCaretPosition - diff
- end
- end
-
- prevTokenWasColored = false
- prevTokenWidth = 0
-
- local tokenType, nextPos = nextToken(code, pos)
-
- if not tokenType or tokenType == tokens.TOKEN_LINEBREAK then
- level = level + preIndent
- if level < 0 then level = 0 end
-
- local s = fillFunction(level, tabWidth)
-
- tsize = tsize + 1
- workingTable[tsize] = s
- totalLen = totalLen + stringlen(s)
-
- if newCaretPosition and not newCaretPositionFinalized then
- newCaretPosition = newCaretPosition + stringlen(s)
- newCaretPositionFinalized = true
- end
-
-
- for k, v in next,workingTable2 do
- tsize = tsize + 1
- workingTable[tsize] = v
- totalLen = totalLen + stringlen(v)
- end
-
- if not tokenType then
- break
- end
-
- tsize = tsize + 1
- workingTable[tsize] = stringsub(code, pos, nextPos - 1)
- totalLen = totalLen + nextPos - pos
-
- level = level + postIndent
- if level < 0 then level = 0 end
-
- tableclear(workingTable2)
- tsize2 = 0
- totalLen2 = 0
-
- hitNonWhitespace = false
- hitIndentRight = false
- preIndent = 0
- postIndent = 0
- elseif tokenType == tokens.TOKEN_WHITESPACE then
- if hitNonWhitespace then
- prevTokenWidth = nextPos - pos
-
- tsize2 = tsize2 + 1
- local s = stringsub(code, pos, nextPos - 1)
- workingTable2[tsize2] = s
- totalLen2 = totalLen2 + stringlen(s)
- end
- elseif tokenType == tokens.TOKEN_COLORCODE_START or tokenType == tokens.TOKEN_COLORCODE_STOP or tokenType == tokens.TOKEN_UNKNOWN then
- -- skip these, though they shouldn't be encountered here anyway
- else
- hitNonWhitespace = true
-
- local str = stringsub(code, pos, nextPos - 1)
-
- prevTokenWidth = nextPos - pos
-
- -- See if this is an indent-modifier
- local indentTable
- if tokenType == tokens.TOKEN_IDENTIFIER then
- indentTable = keywords[str]
- else
- indentTable = tokenIndentation[tokenType]
- end
-
- if indentTable then
- if hitIndentRight then
- postIndent = postIndent + indentTable[1] + indentTable[2]
- else
- local pre = indentTable[1]
- local post = indentTable[2]
- if post > 0 then
- hitIndentRight = true
- end
- preIndent = preIndent + pre
- postIndent = postIndent + post
- end
- end
-
- -- Add coloring
- if keywords[str] then
- tokenType = tokens.TOKEN_KEYWORD
- end
-
- local color
- if stopColor then
- color = colorTable[str]
- if not color then
- color = colorTable[tokenType]
- if not color then
- if tokenType == tokens.TOKEN_IDENTIFIER then
- color = colorTable[tokens.TOKEN_IDENTIFIER]
- else
- color = colorTable[tokens.TOKEN_SPECIAL]
- end
- end
- end
- end
-
- if color then
- tsize2 = tsize2 + 1
- workingTable2[tsize2] = color
- totalLen2 = totalLen2 + stringlen(color)
-
- tsize2 = tsize2 + 1
- workingTable2[tsize2] = str
- totalLen2 = totalLen2 + nextPos - pos
-
- tsize2 = tsize2 + 1
- workingTable2[tsize2] = stopColor
- totalLen2 = totalLen2 + stopColorLen
-
- prevTokenWasColored = true
- else
- tsize2 = tsize2 + 1
- workingTable2[tsize2] = str
- totalLen2 = totalLen2 + nextPos - pos
-
- end
- end
- pos = nextPos
- end
- return table.concat(workingTable), newCaretPosition
- end
-
-
-
- -- WoW specific code:
- local GetTime = GetTime
-
- local editboxSetText
- local editboxGetText
-
- -- Caret code (thanks Tem!)
- local function critical_enter(editbox)
- local script = editbox:GetScript("OnTextSet")
- if script then
- editbox:SetScript("OnTextSet", nil)
- end
- return script
- end
-
- local function critical_leave(editbox, script)
- if script then
- editbox:SetScript("OnTextSet", script)
- end
- end
-
- local function setCaretPos_main(editbox, pos)
- local text = editboxGetText(editbox)
-
- if stringlen(text) > 0 then
- editboxSetText(editbox, stringinsert(text, pos, "a"))
- editbox:HighlightText(pos, pos + 1)
- editbox:Insert("\0")
- end
- end
-
- local function getCaretPos(editbox)
- local script = critical_enter(editbox)
-
- local text = editboxGetText(editbox)
- editbox:Insert("\1")
- local pos = stringfind(editboxGetText(editbox), "\1", 1, 1)
- editboxSetText(editbox, text)
-
- if pos then
- setCaretPos_main(editbox, pos - 1)
- end
- critical_leave(editbox, script)
-
- return (pos or 0) - 1
- end
-
- local function setCaretPos(editbox, pos)
- local script = critical_enter(editbox)
- setCaretPos_main(editbox, pos)
- critical_leave(editbox, script, script2)
- end
- -- end of caret code
-
- function lib.stripWowColors(code)
-
- -- HACK!
- -- This is a fix for a bug, where an unfinished string causes a lot of newlines to be created.
- -- The reason for the bug, is that a |r\n\n gets converted to \n\n|r after the next indent-run
- -- The fix is to remove those last two linebreaks when stripping
- code = stringgsub(code, "|r\n\n$", "|r")
-
- tableclear(workingTable)
- local tsize = 0
-
- local pos = 1
-
- local prevVertical = false
- local even = true
- local selectionStart = 1
-
- while true do
- local byte = stringbyte(code, pos)
- if not byte then
- break
- end
- if byte == bytes.BYTE_VERTICAL then
- even = not even
- prevVertical = true
- else
- if prevVertical and not even then
- if byte == bytes.BYTE_c then
-
- if pos - 2 >= selectionStart then
- tsize = tsize + 1
- workingTable[tsize] = stringsub(code, selectionStart, pos - 2)
- end
-
- pos = pos + 8
- selectionStart = pos + 1
- elseif byte == bytes.BYTE_r then
-
- if pos - 2 >= selectionStart then
- tsize = tsize + 1
- workingTable[tsize] = stringsub(code, selectionStart, pos - 2)
- end
- selectionStart = pos + 1
- end
- end
- prevVertical = false
- even = true
- end
- pos = pos + 1
- end
- if pos >= selectionStart then
- tsize = tsize + 1
- workingTable[tsize] = stringsub(code, selectionStart, pos - 1)
- end
- return table.concat(workingTable)
- end
-
- function lib.decode(code)
- if code then
- code = lib.stripWowColors(code)
- code = stringgsub(code, "||", "|")
- end
- return code or ""
- end
-
- function lib.encode(code)
- if code then
- code = stringgsub(code, "|", "||")
- end
- return code or ""
- end
-
- function lib.stripWowColorsWithPos(code, pos)
- code = stringinsert(code, pos, "\2")
- code = lib.stripWowColors(code)
- pos = stringfind(code, "\2", 1, 1)
- code = stringdelete(code, pos, pos)
- return code, pos
- end
-
- -- returns the padded code, and true if modified, false if unmodified
- local linebreak = stringbyte("\n")
- function lib.padWithLinebreaks(code)
- local len = stringlen(code)
- if stringbyte(code, len) == linebreak then
- if stringbyte(code, len - 1) == linebreak then
- return code, false
- end
- return code .. "\n", true
- end
- return code .. "\n\n", true
-
- end
-
- local defaultTabWidth = 2
- local defaultColorTable
-
- -- Data tables
- -- No weak table magic, since editboxes can never be removed in WoW
- local enabled = {}
- local dirty = {}
-
- local editboxIndentCache = {}
- local decodeCache = {}
- local editboxStringCache = {}
- local editboxNumLinesCache = {}
-
- function lib.colorCodeEditbox(editbox)
- dirty[editbox] = nil
-
- local colorTable = editbox.faiap_colorTable or defaultColorTable
- local tabWidth = editbox.faiap_tabWidth
-
- local orgCode = editboxGetText(editbox)
- local prevCode = editboxStringCache[editbox]
- if prevCode == orgCode then
- return
- end
-
- local pos = getCaretPos(editbox)
-
- local code
- code, pos = lib.stripWowColorsWithPos(orgCode, pos)
-
- colorTable[0] = "|r"
-
- local newCode, newPos, numLines = lib.colorCodeCode(code, colorTable, pos)
- newCode = lib.padWithLinebreaks(newCode)
-
- editboxStringCache[editbox] = newCode
- if orgCode ~= newCode then
- local script, script2 = critical_enter(editbox)
- decodeCache[editbox] = nil
- local stringlenNewCode = stringlen(newCode)
-
- editboxSetText(editbox, newCode)
- if newPos then
- if newPos < 0 then newPos = 0 end
- if newPos > stringlenNewCode then newPos = stringlenNewCode end
-
- setCaretPos(editbox, newPos)
- end
- critical_leave(editbox, script, script2)
- end
-
- if editboxNumLinesCache[editbox] ~= numLines then
- lib.indentEditbox(editbox)
- end
- editboxNumLinesCache[editbox] = numLines
- end
-
- function lib.indentEditbox(editbox)
- dirty[editbox] = nil
-
- local colorTable = editbox.faiap_colorTable or defaultColorTable
- local tabWidth = editbox.faiap_tabWidth
-
- local orgCode = editboxGetText(editbox)
- local prevCode = editboxIndentCache[editbox]
- if prevCode == orgCode then
- return
- end
-
- local pos = getCaretPos(editbox)
-
- local code
- code, pos = lib.stripWowColorsWithPos(orgCode, pos)
-
- colorTable[0] = "|r"
- local newCode, newPos = lib.indentCode(code, tabWidth, colorTable, pos)
- newCode = lib.padWithLinebreaks(newCode)
- editboxIndentCache[editbox] = newCode
- if code ~= newCode then
- local script, script2 = critical_enter(editbox)
- decodeCache[editbox] = nil
-
- local stringlenNewCode = stringlen(newCode)
-
- editboxSetText(editbox, newCode)
-
- if newPos then
- if newPos < 0 then newPos = 0 end
- if newPos > stringlenNewCode then newPos = stringlenNewCode end
-
- setCaretPos(editbox, newPos)
- end
- critical_leave(editbox, script, script2)
- end
- end
-
- local function hookHandler(editbox, handler, newFun)
- local oldFun = editbox:GetScript(handler)
- if oldFun == newFun then
- -- already hooked, ignore it
- return
- end
- editbox["faiap_old_" .. handler] = oldFun
- editbox:SetScript(handler, newFun)
- end
-
- local function textChangedHook(editbox, ...)
- local oldFun = editbox["faiap_old_OnTextChanged"]
- if oldFun then
- oldFun(editbox, ...)
- end
- if enabled[editbox] then
- dirty[editbox] = GetTime()
- end
- end
-
- local function tabPressedHook(editbox, ...)
- local oldFun = editbox["faiap_old_OnTabPressed"]
- if oldFun then
- oldFun(editbox, ...)
- end
- if enabled[editbox] then
- return lib.indentEditbox(editbox)
- end
- end
-
- local function onUpdateHook(editbox, ...)
- local oldFun = editbox["faiap_old_OnUpdate"]
- if oldFun then
- oldFun(editbox, ...)
- end
- if enabled[editbox] then
- local now = GetTime()
- local lastUpdate = dirty[editbox] or now
- if now - lastUpdate > 0.2 then
- decodeCache[editbox] = nil
- return lib.colorCodeEditbox(editbox)
- end
- end
- end
-
- local function newGetText(editbox)
- local decoded = decodeCache[editbox]
- if not decoded then
- decoded = lib.decode(editboxGetText(editbox))
- decodeCache[editbox] = decoded
- end
- return decoded or ""
- end
-
- local function newSetText(editbox, text)
- decodeCache[editbox] = nil
- if text then
- local encoded = lib.encode(text)
-
- return editboxSetText(editbox, encoded)
- end
- end
-
- function lib.enable(editbox, colorTable, tabWidth)
- if not editboxSetText then
- editboxSetText = editbox.SetText
- editboxGetText = editbox.GetText
- end
-
- local modified
- if editbox.faiap_colorTable ~= colorTable then
- editbox.faiap_colorTable = colorTable
- modified = true
- end
- if editbox.faiap_tabWidth ~= tabWidth then
- editbox.faiap_tabWidth = tabWidth
- modified = true
- end
-
- if enabled[editbox] then
- if modified then
- lib.indentEditbox(editbox)
- end
- return
- end
-
- -- Editbox is possibly hooked, but disabled
- enabled[editbox] = true
-
- editbox.oldMaxBytes = editbox:GetMaxBytes()
- editbox.oldMaxLetters = editbox:GetMaxLetters()
- editbox:SetMaxBytes(0)
- editbox:SetMaxLetters(0)
-
- editbox.GetText = newGetText
- editbox.SetText = newSetText
-
- hookHandler(editbox, "OnTextChanged", textChangedHook)
- hookHandler(editbox, "OnTabPressed", tabPressedHook)
- hookHandler(editbox, "OnUpdate", onUpdateHook)
-
- lib.indentEditbox(editbox)
- end
-
- -- Deprecated function
- lib.addSmartCode = lib.enable
-
- function lib.disable(editbox)
- if not enabled[editbox] then
- return
- end
- enabled[editbox] = nil
-
- -- revert settings for max bytes / letters
- editbox:SetMaxBytes(editbox.oldMaxBytes)
- editbox:SetMaxLetters(editbox.oldMaxLetters)
-
- -- try a real unhooking, if possible
- if editbox:GetScript("OnTextChanged") == textChangedHook then
- editbox:SetScript("OnTextChanged", editbox.faiap_old_OnTextChanged)
- editbox.faiap_old_OnTextChanged = nil
- end
-
- if editbox:GetScript("OnTabPressed") == tabPressedHook then
- editbox:SetScript("OnTabPressed", editbox.faiap_old_OnTabPressed)
- editbox.faiap_old_OnTabPressed = nil
- end
-
- if editbox:GetScript("OnUpdate") == onUpdateHook then
- editbox:SetScript("OnUpdate", editbox.faiap_old_OnUpdate)
- editbox.faiap_old_OnUpdate = nil
- end
-
- editbox.GetText = nil
- editbox.SetText = nil
-
- -- change the text back to unformatted
- editbox:SetText(newGetText(editbox))
-
- -- clear caches
- editboxIndentCache[editbox] = nil
- decodeCache[editbox] = nil
- editboxStringCache[editbox] = nil
- editboxNumLinesCache[editbox] = nil
- end
-
- defaultColorTable = {}
- lib.defaultColorTable = defaultColorTable
- defaultColorTable[tokens.TOKEN_SPECIAL] = "|c00ff99ff"
- defaultColorTable[tokens.TOKEN_KEYWORD] = "|c006666ff"
- defaultColorTable[tokens.TOKEN_COMMENT_SHORT] = "|c00999999"
- defaultColorTable[tokens.TOKEN_COMMENT_LONG] = "|c00999999"
-
- local stringColor = "|c00ffff77"
- defaultColorTable[tokens.TOKEN_STRING] = stringColor
- defaultColorTable[".."] = stringColor
-
- local tableColor = "|c00ff9900"
- defaultColorTable["..."] = tableColor
- defaultColorTable["{"] = tableColor
- defaultColorTable["}"] = tableColor
- defaultColorTable["["] = tableColor
- defaultColorTable["]"] = tableColor
-
- local arithmeticColor = "|c0033ff55"
- defaultColorTable[tokens.TOKEN_NUMBER] = arithmeticColor
- defaultColorTable["+"] = arithmeticColor
- defaultColorTable["-"] = arithmeticColor
- defaultColorTable["/"] = arithmeticColor
- defaultColorTable["*"] = arithmeticColor
-
- local logicColor1 = "|c0055ff88"
- defaultColorTable["=="] = logicColor1
- defaultColorTable["<"] = logicColor1
- defaultColorTable["<="] = logicColor1
- defaultColorTable[">"] = logicColor1
- defaultColorTable[">="] = logicColor1
- defaultColorTable["~="] = logicColor1
-
- local logicColor2 = "|c0088ffbb"
- defaultColorTable["and"] = logicColor2
- defaultColorTable["or"] = logicColor2
- defaultColorTable["not"] = logicColor2
-
- defaultColorTable[0] = "|r"
+ local lib = IndentationLib
+ lib.revision = revision
+
+ local stringlen = string.len
+ local stringformat = string.format
+ local stringfind = string.find
+ local stringsub = string.sub
+ local stringbyte = string.byte
+ local stringchar = string.char
+ local stringrep = string.rep
+ local stringgsub = string.gsub
+
+ local defaultTabWidth = 2
+ local defaultColorTable
+
+ local workingTable = {}
+ local workingTable2 = {}
+ local function tableclear(t)
+ for k in next,t do
+ t[k] = nil
+ end
+ end
+
+ local function stringinsert(s, pos, insertStr)
+ return stringsub(s, 1, pos) .. insertStr .. stringsub(s, pos + 1)
+ end
+ lib.stringinsert = stringinsert
+
+ local function stringdelete(s, pos1, pos2)
+ return stringsub(s, 1, pos1 - 1) .. stringsub(s, pos2 + 1)
+ end
+ lib.stringdelete = stringdelete
+
+ -- token types
+ local tokens = {}
+ lib.tokens = tokens
+ tokens.TOKEN_UNKNOWN = 0
+ tokens.TOKEN_NUMBER = 1
+ tokens.TOKEN_LINEBREAK = 2
+ tokens.TOKEN_WHITESPACE = 3
+ tokens.TOKEN_IDENTIFIER = 4
+ tokens.TOKEN_ASSIGNMENT = 5
+ tokens.TOKEN_EQUALITY = 6
+ tokens.TOKEN_MINUS = 7
+ tokens.TOKEN_COMMENT_SHORT = 8
+ tokens.TOKEN_COMMENT_LONG = 9
+ tokens.TOKEN_STRING = 10
+ tokens.TOKEN_LEFTBRACKET = 11
+ tokens.TOKEN_PERIOD = 12
+ tokens.TOKEN_DOUBLEPERIOD = 13
+ tokens.TOKEN_TRIPLEPERIOD = 14
+ tokens.TOKEN_LTE = 15
+ tokens.TOKEN_LT = 16
+ tokens.TOKEN_GTE = 17
+ tokens.TOKEN_GT = 18
+ tokens.TOKEN_NOTEQUAL = 19
+ tokens.TOKEN_COMMA = 20
+ tokens.TOKEN_SEMICOLON = 21
+ tokens.TOKEN_COLON = 22
+ tokens.TOKEN_LEFTPAREN = 23
+ tokens.TOKEN_RIGHTPAREN = 24
+ tokens.TOKEN_PLUS = 25
+ tokens.TOKEN_SLASH = 27
+ tokens.TOKEN_LEFTWING = 28
+ tokens.TOKEN_RIGHTWING = 29
+ tokens.TOKEN_CIRCUMFLEX = 30
+ tokens.TOKEN_ASTERISK = 31
+ tokens.TOKEN_RIGHTBRACKET = 32
+ tokens.TOKEN_KEYWORD = 33
+ tokens.TOKEN_SPECIAL = 34
+ tokens.TOKEN_VERTICAL = 35
+ tokens.TOKEN_TILDE = 36
+ -- WoW specific tokens
+ tokens.TOKEN_COLORCODE_START = 37
+ tokens.TOKEN_COLORCODE_STOP = 38
+ -- new as of lua 5.1
+ tokens.TOKEN_HASH = 39
+ tokens.TOKEN_PERCENT = 40
+
+
+ -- ascii codes
+ local bytes = {}
+ lib.bytes = bytes
+ bytes.BYTE_LINEBREAK_UNIX = stringbyte("\n")
+ bytes.BYTE_LINEBREAK_MAC = stringbyte("\r")
+ bytes.BYTE_SINGLE_QUOTE = stringbyte("'")
+ bytes.BYTE_DOUBLE_QUOTE = stringbyte('"')
+ bytes.BYTE_0 = stringbyte("0")
+ bytes.BYTE_9 = stringbyte("9")
+ bytes.BYTE_PERIOD = stringbyte(".")
+ bytes.BYTE_SPACE = stringbyte(" ")
+ bytes.BYTE_TAB = stringbyte("\t")
+ bytes.BYTE_E = stringbyte("E")
+ bytes.BYTE_e = stringbyte("e")
+ bytes.BYTE_MINUS = stringbyte("-")
+ bytes.BYTE_EQUALS = stringbyte("=")
+ bytes.BYTE_LEFTBRACKET = stringbyte("[")
+ bytes.BYTE_RIGHTBRACKET = stringbyte("]")
+ bytes.BYTE_BACKSLASH = stringbyte("\\")
+ bytes.BYTE_COMMA = stringbyte(",")
+ bytes.BYTE_SEMICOLON = stringbyte(";")
+ bytes.BYTE_COLON = stringbyte(":")
+ bytes.BYTE_LEFTPAREN = stringbyte("(")
+ bytes.BYTE_RIGHTPAREN = stringbyte(")")
+ bytes.BYTE_TILDE = stringbyte("~")
+ bytes.BYTE_PLUS = stringbyte("+")
+ bytes.BYTE_SLASH = stringbyte("/")
+ bytes.BYTE_LEFTWING = stringbyte("{")
+ bytes.BYTE_RIGHTWING = stringbyte("}")
+ bytes.BYTE_CIRCUMFLEX = stringbyte("^")
+ bytes.BYTE_ASTERISK = stringbyte("*")
+ bytes.BYTE_LESSTHAN = stringbyte("<")
+ bytes.BYTE_GREATERTHAN = stringbyte(">")
+ -- WoW specific chars
+ bytes.BYTE_VERTICAL = stringbyte("|")
+ bytes.BYTE_r = stringbyte("r")
+ bytes.BYTE_c = stringbyte("c")
+ -- new as of lua 5.1
+ bytes.BYTE_HASH = stringbyte("#")
+ bytes.BYTE_PERCENT = stringbyte("%")
+
+
+ local linebreakCharacters = {}
+ lib.linebreakCharacters = linebreakCharacters
+ linebreakCharacters[bytes.BYTE_LINEBREAK_UNIX] = 1
+ linebreakCharacters[bytes.BYTE_LINEBREAK_MAC] = 1
+
+ local whitespaceCharacters = {}
+ lib.whitespaceCharacters = whitespaceCharacters
+ whitespaceCharacters[bytes.BYTE_SPACE] = 1
+ whitespaceCharacters[bytes.BYTE_TAB] = 1
+
+ local specialCharacters = {}
+ lib.specialCharacters = specialCharacters
+ specialCharacters[bytes.BYTE_PERIOD] = -1
+ specialCharacters[bytes.BYTE_LESSTHAN] = -1
+ specialCharacters[bytes.BYTE_GREATERTHAN] = -1
+ specialCharacters[bytes.BYTE_LEFTBRACKET] = -1
+ specialCharacters[bytes.BYTE_EQUALS] = -1
+ specialCharacters[bytes.BYTE_MINUS] = -1
+ specialCharacters[bytes.BYTE_SINGLE_QUOTE] = -1
+ specialCharacters[bytes.BYTE_DOUBLE_QUOTE] = -1
+ specialCharacters[bytes.BYTE_TILDE] = -1
+ specialCharacters[bytes.BYTE_RIGHTBRACKET] = tokens.TOKEN_RIGHTBRACKET
+ specialCharacters[bytes.BYTE_COMMA] = tokens.TOKEN_COMMA
+ specialCharacters[bytes.BYTE_COLON] = tokens.TOKEN_COLON
+ specialCharacters[bytes.BYTE_SEMICOLON] = tokens.TOKEN_SEMICOLON
+ specialCharacters[bytes.BYTE_LEFTPAREN] = tokens.TOKEN_LEFTPAREN
+ specialCharacters[bytes.BYTE_RIGHTPAREN] = tokens.TOKEN_RIGHTPAREN
+ specialCharacters[bytes.BYTE_PLUS] = tokens.TOKEN_PLUS
+ specialCharacters[bytes.BYTE_SLASH] = tokens.TOKEN_SLASH
+ specialCharacters[bytes.BYTE_LEFTWING] = tokens.TOKEN_LEFTWING
+ specialCharacters[bytes.BYTE_RIGHTWING] = tokens.TOKEN_RIGHTWING
+ specialCharacters[bytes.BYTE_CIRCUMFLEX] = tokens.TOKEN_CIRCUMFLEX
+ specialCharacters[bytes.BYTE_ASTERISK] = tokens.TOKEN_ASTERISK
+ -- WoW specific
+ specialCharacters[bytes.BYTE_VERTICAL] = -1
+ -- new as of lua 5.1
+ specialCharacters[bytes.BYTE_HASH] = tokens.TOKEN_HASH
+ specialCharacters[bytes.BYTE_PERCENT] = tokens.TOKEN_PERCENT
+
+ local function nextNumberExponentPartInt(text, pos)
+ while true do
+ local byte = stringbyte(text, pos)
+ if not byte then
+ return tokens.TOKEN_NUMBER, pos
+ end
+
+ if byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
+ pos = pos + 1
+ else
+ return tokens.TOKEN_NUMBER, pos
+ end
+ end
+ end
+
+ local function nextNumberExponentPart(text, pos)
+ local byte = stringbyte(text, pos)
+ if not byte then
+ return tokens.TOKEN_NUMBER, pos
+ end
+
+ if byte == bytes.BYTE_MINUS then
+ -- handle this case: a = 1.2e-- some comment
+ -- i decide to let 1.2e be parsed as a a number
+ byte = stringbyte(text, pos + 1)
+ if byte == bytes.BYTE_MINUS then
+ return tokens.TOKEN_NUMBER, pos
+ end
+ return nextNumberExponentPartInt(text, pos + 1)
+ end
+
+ return nextNumberExponentPartInt(text, pos)
+ end
+
+ local function nextNumberFractionPart(text, pos)
+ while true do
+ local byte = stringbyte(text, pos)
+ if not byte then
+ return tokens.TOKEN_NUMBER, pos
+ end
+
+ if byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
+ pos = pos + 1
+ elseif byte == bytes.BYTE_E or byte == bytes.BYTE_e then
+ return nextNumberExponentPart(text, pos + 1)
+ else
+ return tokens.TOKEN_NUMBER, pos
+ end
+ end
+ end
+
+ local function nextNumberIntPart(text, pos)
+ while true do
+ local byte = stringbyte(text, pos)
+ if not byte then
+ return tokens.TOKEN_NUMBER, pos
+ end
+
+ if byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
+ pos = pos + 1
+ elseif byte == bytes.BYTE_PERIOD then
+ return nextNumberFractionPart(text, pos + 1)
+ elseif byte == bytes.BYTE_E or byte == bytes.BYTE_e then
+ return nextNumberExponentPart(text, pos + 1)
+ else
+ return tokens.TOKEN_NUMBER, pos
+ end
+ end
+ end
+
+ local function nextIdentifier(text, pos)
+ while true do
+ local byte = stringbyte(text, pos)
+
+ if not byte or
+ linebreakCharacters[byte] or
+ whitespaceCharacters[byte] or
+ specialCharacters[byte] then
+ return tokens.TOKEN_IDENTIFIER, pos
+ end
+ pos = pos + 1
+ end
+ end
+
+ -- returns false or: true, nextPos, equalsCount
+ local function isBracketStringNext(text, pos)
+ local byte = stringbyte(text, pos)
+ if byte == bytes.BYTE_LEFTBRACKET then
+ local pos2 = pos + 1
+ byte = stringbyte(text, pos2)
+ while byte == bytes.BYTE_EQUALS do
+ pos2 = pos2 + 1
+ byte = stringbyte(text, pos2)
+ end
+ if byte == bytes.BYTE_LEFTBRACKET then
+ return true, pos2 + 1, (pos2 - 1) - pos
+ else
+ return false
+ end
+ else
+ return false
+ end
+ end
+
+ -- Already parsed the [==[ part when get here
+ local function nextBracketString(text, pos, equalsCount)
+ local state = 0
+ while true do
+ local byte = stringbyte(text, pos)
+ if not byte then
+ return tokens.TOKEN_STRING, pos
+ end
+
+ if byte == bytes.BYTE_RIGHTBRACKET then
+ if state == 0 then
+ state = 1
+ elseif state == equalsCount + 1 then
+ return tokens.TOKEN_STRING, pos + 1
+ else
+ state = 0
+ end
+ elseif byte == bytes.BYTE_EQUALS then
+ if state > 0 then
+ state = state + 1
+ end
+ else
+ state = 0
+ end
+ pos = pos + 1
+ end
+ end
+
+ local function nextComment(text, pos)
+ -- When we get here we have already parsed the "--"
+ -- Check for long comment
+ local isBracketString, nextPos, equalsCount = isBracketStringNext(text, pos)
+ if isBracketString then
+ local tokenType, nextPos2 = nextBracketString(text, nextPos, equalsCount)
+ return tokens.TOKEN_COMMENT_LONG, nextPos2
+ end
+
+ local byte = stringbyte(text, pos)
+
+ -- Short comment, find the first linebreak
+ while true do
+ byte = stringbyte(text, pos)
+ if not byte then
+ return tokens.TOKEN_COMMENT_SHORT, pos
+ end
+ if linebreakCharacters[byte] then
+ return tokens.TOKEN_COMMENT_SHORT, pos
+ end
+ pos = pos + 1
+ end
+ end
+
+ local function nextString(text, pos, character)
+ local even = true
+ while true do
+ local byte = stringbyte(text, pos)
+ if not byte then
+ return tokens.TOKEN_STRING, pos
+ end
+
+ if byte == character then
+ if even then
+ return tokens.TOKEN_STRING, pos + 1
+ end
+ end
+ if byte == bytes.BYTE_BACKSLASH then
+ even = not even
+ else
+ even = true
+ end
+
+ pos = pos + 1
+ end
+ end
+
+ -- INPUT
+ -- 1: text: text to search in
+ -- 2: tokenPos: where to start searching
+ -- OUTPUT
+ -- 1: token type
+ -- 2: position after the last character of the token
+ local function nextToken(text, pos)
+ local byte = stringbyte(text, pos)
+ if not byte then
+ return nil
+ end
+
+ if linebreakCharacters[byte] then
+ return tokens.TOKEN_LINEBREAK, pos + 1
+ end
+
+ if whitespaceCharacters[byte] then
+ while true do
+ pos = pos + 1
+ byte = stringbyte(text, pos)
+ if not byte or not whitespaceCharacters[byte] then
+ return tokens.TOKEN_WHITESPACE, pos
+ end
+ end
+ end
+
+ local token = specialCharacters[byte]
+ if token then
+ if token ~= -1 then
+ return token, pos + 1
+ end
+
+ -- WoW specific (for color codes)
+ if byte == bytes.BYTE_VERTICAL then
+ byte = stringbyte(text, pos + 1)
+ if byte == bytes.BYTE_VERTICAL then
+ return tokens.TOKEN_VERTICAL, pos + 2
+ end
+ if byte == bytes.BYTE_c then
+ return tokens.TOKEN_COLORCODE_START, pos + 10
+ end
+ if byte == bytes.BYTE_r then
+ return tokens.TOKEN_COLORCODE_STOP, pos + 2
+ end
+ return tokens.TOKEN_UNKNOWN, pos + 1
+ end
+
+ if byte == bytes.BYTE_MINUS then
+ byte = stringbyte(text, pos + 1)
+ if byte == bytes.BYTE_MINUS then
+ return nextComment(text, pos + 2)
+ end
+ return tokens.TOKEN_MINUS, pos + 1
+ end
+
+ if byte == bytes.BYTE_SINGLE_QUOTE then
+ return nextString(text, pos + 1, bytes.BYTE_SINGLE_QUOTE)
+ end
+
+ if byte == bytes.BYTE_DOUBLE_QUOTE then
+ return nextString(text, pos + 1, bytes.BYTE_DOUBLE_QUOTE)
+ end
+
+ if byte == bytes.BYTE_LEFTBRACKET then
+ local isBracketString, nextPos, equalsCount = isBracketStringNext(text, pos)
+ if isBracketString then
+ return nextBracketString(text, nextPos, equalsCount)
+ else
+ return tokens.TOKEN_LEFTBRACKET, pos + 1
+ end
+ end
+
+ if byte == bytes.BYTE_EQUALS then
+ byte = stringbyte(text, pos + 1)
+ if not byte then
+ return tokens.TOKEN_ASSIGNMENT, pos + 1
+ end
+ if byte == bytes.BYTE_EQUALS then
+ return tokens.TOKEN_EQUALITY, pos + 2
+ end
+ return tokens.TOKEN_ASSIGNMENT, pos + 1
+ end
+
+ if byte == bytes.BYTE_PERIOD then
+ byte = stringbyte(text, pos + 1)
+ if not byte then
+ return tokens.TOKEN_PERIOD, pos + 1
+ end
+ if byte == bytes.BYTE_PERIOD then
+ byte = stringbyte(text, pos + 2)
+ if byte == bytes.BYTE_PERIOD then
+ return tokens.TOKEN_TRIPLEPERIOD, pos + 3
+ end
+ return tokens.TOKEN_DOUBLEPERIOD, pos + 2
+ elseif byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
+ return nextNumberFractionPart(text, pos + 2)
+ end
+ return tokens.TOKEN_PERIOD, pos + 1
+ end
+
+ if byte == bytes.BYTE_LESSTHAN then
+ byte = stringbyte(text, pos + 1)
+ if byte == bytes.BYTE_EQUALS then
+ return tokens.TOKEN_LTE, pos + 2
+ end
+ return tokens.TOKEN_LT, pos + 1
+ end
+
+ if byte == bytes.BYTE_GREATERTHAN then
+ byte = stringbyte(text, pos + 1)
+ if byte == bytes.BYTE_EQUALS then
+ return tokens.TOKEN_GTE, pos + 2
+ end
+ return tokens.TOKEN_GT, pos + 1
+ end
+
+ if byte == bytes.BYTE_TILDE then
+ byte = stringbyte(text, pos + 1)
+ if byte == bytes.BYTE_EQUALS then
+ return tokens.TOKEN_NOTEQUAL, pos + 2
+ end
+ return tokens.TOKEN_TILDE, pos + 1
+ end
+
+ return tokens.TOKEN_UNKNOWN, pos + 1
+ elseif byte >= bytes.BYTE_0 and byte <= bytes.BYTE_9 then
+ return nextNumberIntPart(text, pos + 1)
+ else
+ return nextIdentifier(text, pos + 1)
+ end
+ end
+
+ -- Cool stuff begins here! (indentation and highlighting)
+
+ local noIndentEffect = {0, 0}
+ local indentLeft = {-1, 0}
+ local indentRight = {0, 1}
+ local indentBoth = {-1, 1}
+
+ local keywords = {}
+ lib.keywords = keywords
+ keywords["and"] = noIndentEffect
+ keywords["break"] = noIndentEffect
+ keywords["false"] = noIndentEffect
+ keywords["for"] = noIndentEffect
+ keywords["if"] = noIndentEffect
+ keywords["in"] = noIndentEffect
+ keywords["local"] = noIndentEffect
+ keywords["nil"] = noIndentEffect
+ keywords["not"] = noIndentEffect
+ keywords["or"] = noIndentEffect
+ keywords["return"] = noIndentEffect
+ keywords["true"] = noIndentEffect
+ keywords["while"] = noIndentEffect
+
+ keywords["until"] = indentLeft
+ keywords["elseif"] = indentLeft
+ keywords["end"] = indentLeft
+
+ keywords["do"] = indentRight
+ keywords["then"] = indentRight
+ keywords["repeat"] = indentRight
+ keywords["function"] = indentRight
+
+ keywords["else"] = indentBoth
+
+ local tokenIndentation = {}
+ lib.tokenIndentation = tokenIndentation
+ tokenIndentation[tokens.TOKEN_LEFTPAREN] = indentRight
+ tokenIndentation[tokens.TOKEN_LEFTBRACKET] = indentRight
+ tokenIndentation[tokens.TOKEN_LEFTWING] = indentRight
+
+ tokenIndentation[tokens.TOKEN_RIGHTPAREN] = indentLeft
+ tokenIndentation[tokens.TOKEN_RIGHTBRACKET] = indentLeft
+ tokenIndentation[tokens.TOKEN_RIGHTWING] = indentLeft
+
+ local function fillWithTabs(n)
+ return stringrep("\t", n)
+ end
+
+ local function fillWithSpaces(a, b)
+ return stringrep(" ", a*b)
+ end
+
+ function lib.colorCodeCode(code, colorTable, caretPosition)
+ local stopColor = colorTable and colorTable[0]
+ if not stopColor then
+ return code, caretPosition
+ end
+
+ local stopColorLen = stringlen(stopColor)
+
+ tableclear(workingTable)
+ local tsize = 0
+ local totalLen = 0
+
+ local numLines = 0
+ local newCaretPosition
+ local prevTokenWasColored = false
+ local prevTokenWidth = 0
+
+ local pos = 1
+ local level = 0
+
+ while true do
+ if caretPosition and not newCaretPosition and pos >= caretPosition then
+ if pos == caretPosition then
+ newCaretPosition = totalLen
+ else
+ newCaretPosition = totalLen
+ local diff = pos - caretPosition
+ if diff > prevTokenWidth then
+ diff = prevTokenWidth
+ end
+ if prevTokenWasColored then
+ diff = diff + stopColorLen
+ end
+ newCaretPosition = newCaretPosition - diff
+ end
+ end
+
+ prevTokenWasColored = false
+ prevTokenWidth = 0
+
+ local tokenType, nextPos = nextToken(code, pos)
+
+ if not tokenType then
+ break
+ end
+
+ if tokenType == tokens.TOKEN_COLORCODE_START or tokenType == tokens.TOKEN_COLORCODE_STOP or tokenType == tokens.TOKEN_UNKNOWN then
+ -- ignore color codes
+ elseif tokenType == tokens.TOKEN_LINEBREAK or tokenType == tokens.TOKEN_WHITESPACE then
+ if tokenType == tokens.TOKEN_LINEBREAK then
+ numLines = numLines + 1
+ end
+ local str = stringsub(code, pos, nextPos - 1)
+ prevTokenWidth = nextPos - pos
+
+ tsize = tsize + 1
+ workingTable[tsize] = str
+ totalLen = totalLen + stringlen(str)
+ else
+ local str = stringsub(code, pos, nextPos - 1)
+
+ prevTokenWidth = nextPos - pos
+
+ -- Add coloring
+ if keywords[str] then
+ tokenType = tokens.TOKEN_KEYWORD
+ end
+
+ local color
+ if stopColor then
+ color = colorTable[str]
+ if not color then
+ color = colorTable[tokenType]
+ if not color then
+ if tokenType == tokens.TOKEN_IDENTIFIER then
+ color = colorTable[tokens.TOKEN_IDENTIFIER]
+ else
+ color = colorTable[tokens.TOKEN_SPECIAL]
+ end
+ end
+ end
+ end
+
+ if color then
+ tsize = tsize + 1
+ workingTable[tsize] = color
+ tsize = tsize + 1
+ workingTable[tsize] = str
+ tsize = tsize + 1
+ workingTable[tsize] = stopColor
+
+ totalLen = totalLen + stringlen(color) + (nextPos - pos) + stopColorLen
+ prevTokenWasColored = true
+ else
+ tsize = tsize + 1
+ workingTable[tsize] = str
+
+ totalLen = totalLen + stringlen(str)
+ end
+ end
+
+ pos = nextPos
+ end
+ return table.concat(workingTable), newCaretPosition, numLines
+ end
+
+ function lib.indentCode(code, tabWidth, colorTable, caretPosition)
+ local fillFunction
+ if tabWidth == nil then
+ tabWidth = defaultTabWidth
+ end
+ if tabWidth then
+ fillFunction = fillWithSpaces
+ else
+ fillFunction = fillWithTabs
+ end
+
+ tableclear(workingTable)
+ local tsize = 0
+ local totalLen = 0
+
+ tableclear(workingTable2)
+ local tsize2 = 0
+ local totalLen2 = 0
+
+ local stopColor = colorTable and colorTable[0]
+ local stopColorLen = not stopColor or stringlen(stopColor)
+
+ local newCaretPosition
+ local newCaretPositionFinalized = false
+ local prevTokenWasColored = false
+ local prevTokenWidth = 0
+
+ local pos = 1
+ local level = 0
+
+ local hitNonWhitespace = false
+ local hitIndentRight = false
+ local preIndent = 0
+ local postIndent = 0
+ while true do
+ if caretPosition and not newCaretPosition and pos >= caretPosition then
+ if pos == caretPosition then
+ newCaretPosition = totalLen + totalLen2
+ else
+ newCaretPosition = totalLen + totalLen2
+ local diff = pos - caretPosition
+ if diff > prevTokenWidth then
+ diff = prevTokenWidth
+ end
+ if prevTokenWasColored then
+ diff = diff + stopColorLen
+ end
+ newCaretPosition = newCaretPosition - diff
+ end
+ end
+
+ prevTokenWasColored = false
+ prevTokenWidth = 0
+
+ local tokenType, nextPos = nextToken(code, pos)
+
+ if not tokenType or tokenType == tokens.TOKEN_LINEBREAK then
+ level = level + preIndent
+ if level < 0 then level = 0 end
+
+ local s = fillFunction(level, tabWidth)
+
+ tsize = tsize + 1
+ workingTable[tsize] = s
+ totalLen = totalLen + stringlen(s)
+
+ if newCaretPosition and not newCaretPositionFinalized then
+ newCaretPosition = newCaretPosition + stringlen(s)
+ newCaretPositionFinalized = true
+ end
+
+ for k, v in next,workingTable2 do
+ tsize = tsize + 1
+ workingTable[tsize] = v
+ totalLen = totalLen + stringlen(v)
+ end
+
+ if not tokenType then
+ break
+ end
+
+ tsize = tsize + 1
+ workingTable[tsize] = stringsub(code, pos, nextPos - 1)
+ totalLen = totalLen + nextPos - pos
+
+ level = level + postIndent
+ if level < 0 then level = 0 end
+
+ tableclear(workingTable2)
+ tsize2 = 0
+ totalLen2 = 0
+
+ hitNonWhitespace = false
+ hitIndentRight = false
+ preIndent = 0
+ postIndent = 0
+ elseif tokenType == tokens.TOKEN_WHITESPACE then
+ if hitNonWhitespace then
+ prevTokenWidth = nextPos - pos
+ tsize2 = tsize2 + 1
+ local s = stringsub(code, pos, nextPos - 1)
+ workingTable2[tsize2] = s
+ totalLen2 = totalLen2 + stringlen(s)
+ end
+ elseif tokenType == tokens.TOKEN_COLORCODE_START or tokenType == tokens.TOKEN_COLORCODE_STOP or tokenType == tokens.TOKEN_UNKNOWN then
+ -- skip these, though they shouldn't be encountered here anyway
+ else
+ hitNonWhitespace = true
+ local str = stringsub(code, pos, nextPos - 1)
+ prevTokenWidth = nextPos - pos
+
+ -- See if this is an indent-modifier
+ local indentTable
+ if tokenType == tokens.TOKEN_IDENTIFIER then
+ indentTable = keywords[str]
+ else
+ indentTable = lib.tokenIndentation[tokenType]
+ end
+
+ if indentTable then
+ if hitIndentRight then
+ postIndent = postIndent + indentTable[1] + indentTable[2]
+ else
+ local pre = indentTable[1]
+ local post = indentTable[2]
+ if post > 0 then
+ hitIndentRight = true
+ end
+ preIndent = preIndent + pre
+ postIndent = postIndent + post
+ end
+ end
+
+ -- Add coloring
+ if keywords[str] then
+ tokenType = tokens.TOKEN_KEYWORD
+ end
+
+ local color
+ if stopColor then
+ color = colorTable[str]
+ if not color then
+ color = colorTable[tokenType]
+ if not color then
+ if tokenType == tokens.TOKEN_IDENTIFIER then
+ color = colorTable[tokens.TOKEN_IDENTIFIER]
+ else
+ color = colorTable[tokens.TOKEN_SPECIAL]
+ end
+ end
+ end
+ end
+
+ if color then
+ tsize2 = tsize2 + 1
+ workingTable2[tsize2] = color
+ totalLen2 = totalLen2 + stringlen(color)
+
+ tsize2 = tsize2 + 1
+ workingTable2[tsize2] = str
+ totalLen2 = totalLen2 + nextPos - pos
+
+ tsize2 = tsize2 + 1
+ workingTable2[tsize2] = stopColor
+ totalLen2 = totalLen2 + stopColorLen
+
+ prevTokenWasColored = true
+ else
+ tsize2 = tsize2 + 1
+ workingTable2[tsize2] = str
+ totalLen2 = totalLen2 + nextPos - pos
+
+ end
+ end
+ pos = nextPos
+ end
+ return table.concat(workingTable), newCaretPosition
+ end
+
+ -- WoW specific code:
+ local GetTime = GetTime
+
+ local editboxSetText
+ local editboxGetText
+
+ -- Caret code (thanks Tem!)
+ local function critical_enter(editbox)
+ local script = editbox:GetScript("OnTextSet")
+ if script then
+ editbox:SetScript("OnTextSet", nil)
+ end
+ return script
+ end
+
+ local function critical_leave(editbox, script)
+ if script then
+ editbox:SetScript("OnTextSet", script)
+ end
+ end
+
+ local function setCaretPos_main(editbox, pos)
+ local text = editboxGetText(editbox)
+
+ if stringlen(text) > 0 then
+ editboxSetText(editbox, stringinsert(text, pos, "a"))
+ editbox:HighlightText(pos, pos + 1)
+ editbox:Insert("\0")
+ end
+ end
+
+ local function getCaretPos(editbox)
+ local script = critical_enter(editbox)
+
+ local text = editboxGetText(editbox)
+ editbox:Insert("")
+ local pos = stringfind(editboxGetText(editbox), "", 1, 1)
+ editboxSetText(editbox, text)
+
+ if pos then
+ setCaretPos_main(editbox, pos - 1)
+ end
+ critical_leave(editbox, script)
+
+ return (pos or 0) - 1
+ end
+
+ local function setCaretPos(editbox, pos)
+ local script, script2 = critical_enter(editbox)
+ setCaretPos_main(editbox, pos)
+ critical_leave(editbox, script, script2)
+ end
+ -- end of caret code
+
+ function lib.stripWowColors(code)
+
+ -- HACK!
+ -- This is a fix for a bug, where an unfinished string causes a lot of newlines to be created.
+ -- The reason for the bug, is that a |r\n\n gets converted to \n\n|r after the next indent-run
+ -- The fix is to remove those last two linebreaks when stripping
+ code = stringgsub(code, "|r\n\n$", "|r")
+
+ tableclear(workingTable)
+ local tsize = 0
+
+ local pos = 1
+
+ local prevVertical = false
+ local even = true
+ local selectionStart = 1
+
+ while true do
+ local byte = stringbyte(code, pos)
+ if not byte then
+ break
+ end
+ if byte == bytes.BYTE_VERTICAL then
+ even = not even
+ prevVertical = true
+ else
+ if prevVertical and not even then
+ if byte == bytes.BYTE_c then
+
+ if pos - 2 >= selectionStart then
+ tsize = tsize + 1
+ workingTable[tsize] = stringsub(code, selectionStart, pos - 2)
+ end
+
+ pos = pos + 8
+ selectionStart = pos + 1
+ elseif byte == bytes.BYTE_r then
+
+ if pos - 2 >= selectionStart then
+ tsize = tsize + 1
+ workingTable[tsize] = stringsub(code, selectionStart, pos - 2)
+ end
+ selectionStart = pos + 1
+ end
+ end
+ prevVertical = false
+ even = true
+ end
+ pos = pos + 1
+ end
+ if pos >= selectionStart then
+ tsize = tsize + 1
+ workingTable[tsize] = stringsub(code, selectionStart, pos - 1)
+ end
+ return table.concat(workingTable)
+ end
+
+ function lib.decode(code)
+ if code then
+ code = lib.stripWowColors(code)
+ code = stringgsub(code, "||", "|")
+ end
+ return code or ""
+ end
+
+ function lib.encode(code)
+ if code then
+ code = stringgsub(code, "|", "||")
+ end
+ return code or ""
+ end
+
+ function lib.stripWowColorsWithPos(code, pos)
+ code = stringinsert(code, pos, "\2")
+ code = lib.stripWowColors(code)
+ pos = stringfind(code, "\2", 1, 1)
+ code = stringdelete(code, pos, pos)
+ return code, pos
+ end
+
+ -- returns the padded code, and true if modified, false if unmodified
+ local linebreak = stringbyte("\n")
+ function lib.padWithLinebreaks(code)
+ local len = stringlen(code)
+ if stringbyte(code, len) == linebreak then
+ if stringbyte(code, len - 1) == linebreak then
+ return code, false
+ end
+ return code .. "\n", true
+ end
+ return code .. "\n\n", true
+
+ end
+
+ -- Data tables
+ -- No weak table magic, since editboxes can never be removed in WoW
+ local enabled = {}
+ local dirty = {}
+
+ local editboxIndentCache = {}
+ local decodeCache = {}
+ local editboxStringCache = {}
+ local editboxNumLinesCache = {}
+
+ function lib.colorCodeEditbox(editbox)
+ dirty[editbox] = nil
+
+ local colorTable = editbox.faiap_colorTable or defaultColorTable
+ local tabWidth = editbox.faiap_tabWidth
+
+ local orgCode = editboxGetText(editbox)
+ local prevCode = editboxStringCache[editbox]
+ if prevCode == orgCode then
+ return
+ end
+
+ local pos = getCaretPos(editbox)
+
+ local code
+ code, pos = lib.stripWowColorsWithPos(orgCode, pos)
+
+ colorTable[0] = "|r"
+
+ local newCode, newPos, numLines = lib.colorCodeCode(code, colorTable, pos)
+ newCode = lib.padWithLinebreaks(newCode)
+
+ editboxStringCache[editbox] = newCode
+ if orgCode ~= newCode then
+ local script, script2 = critical_enter(editbox)
+ decodeCache[editbox] = nil
+ local stringlenNewCode = stringlen(newCode)
+
+ editboxSetText(editbox, newCode)
+ if newPos then
+ if newPos < 0 then newPos = 0 end
+ if newPos > stringlenNewCode then newPos = stringlenNewCode end
+
+ setCaretPos(editbox, newPos)
+ end
+ critical_leave(editbox, script, script2)
+ end
+
+ if editboxNumLinesCache[editbox] ~= numLines then
+ lib.indentEditbox(editbox)
+ end
+ editboxNumLinesCache[editbox] = numLines
+ end
+
+ function lib.indentEditbox(editbox)
+ dirty[editbox] = nil
+
+ local colorTable = editbox.faiap_colorTable or defaultColorTable
+ local tabWidth = editbox.faiap_tabWidth
+
+ local orgCode = editboxGetText(editbox)
+ local prevCode = editboxIndentCache[editbox]
+ if prevCode == orgCode then
+ return
+ end
+
+ local pos = getCaretPos(editbox)
+
+ local code
+ code, pos = lib.stripWowColorsWithPos(orgCode, pos)
+
+ colorTable[0] = "|r"
+ local newCode, newPos = lib.indentCode(code, tabWidth, colorTable, pos)
+ newCode = lib.padWithLinebreaks(newCode)
+ editboxIndentCache[editbox] = newCode
+ if code ~= newCode then
+ local script, script2 = critical_enter(editbox)
+ decodeCache[editbox] = nil
+
+ local stringlenNewCode = stringlen(newCode)
+
+ editboxSetText(editbox, newCode)
+
+ if newPos then
+ if newPos < 0 then newPos = 0 end
+ if newPos > stringlenNewCode then newPos = stringlenNewCode end
+
+ setCaretPos(editbox, newPos)
+ end
+ critical_leave(editbox, script, script2)
+ end
+ end
+
+ local function hookHandler(editbox, handler, newFun)
+ local oldFun = editbox:GetScript(handler)
+ if oldFun == newFun then
+ -- already hooked, ignore it
+ return
+ end
+ editbox["faiap_old_" .. handler] = oldFun
+ editbox:SetScript(handler, newFun)
+ end
+
+ local function textChangedHook(editbox, ...)
+ local oldFun = editbox["faiap_old_OnTextChanged"]
+ if oldFun then
+ oldFun(editbox, ...)
+ end
+ if enabled[editbox] then
+ dirty[editbox] = GetTime()
+ end
+ end
+
+ local function tabPressedHook(editbox, ...)
+ local oldFun = editbox["faiap_old_OnTabPressed"]
+ if oldFun then
+ oldFun(editbox, ...)
+ end
+ if enabled[editbox] then
+ return lib.indentEditbox(editbox)
+ end
+ end
+
+ local function onUpdateHook(editbox, ...)
+ local oldFun = editbox["faiap_old_OnUpdate"]
+ if oldFun then
+ oldFun(editbox, ...)
+ end
+ if enabled[editbox] then
+ local now = GetTime()
+ local lastUpdate = dirty[editbox] or now
+ if now - lastUpdate > 0.2 then
+ decodeCache[editbox] = nil
+ return lib.colorCodeEditbox(editbox)
+ end
+ end
+ end
+
+ local function newGetText(editbox)
+ local decoded = decodeCache[editbox]
+ if not decoded then
+ decoded = lib.decode(editboxGetText(editbox))
+ decodeCache[editbox] = decoded
+ end
+ return decoded or ""
+ end
+
+ local function newSetText(editbox, text)
+ decodeCache[editbox] = nil
+ if text then
+ local encoded = lib.encode(text)
+
+ return editboxSetText(editbox, encoded)
+ end
+ end
+
+ function lib.enable(editbox, colorTable, tabWidth)
+ if not editboxSetText then
+ editboxSetText = editbox.SetText
+ editboxGetText = editbox.GetText
+ end
+
+ local modified
+ if editbox.faiap_colorTable ~= colorTable then
+ editbox.faiap_colorTable = colorTable
+ modified = true
+ end
+ if editbox.faiap_tabWidth ~= tabWidth then
+ editbox.faiap_tabWidth = tabWidth
+ modified = true
+ end
+
+ if enabled[editbox] then
+ if modified then
+ lib.indentEditbox(editbox)
+ end
+ return
+ end
+
+ -- Editbox is possibly hooked, but disabled
+ enabled[editbox] = true
+
+ editbox.oldMaxBytes = editbox:GetMaxBytes()
+ editbox.oldMaxLetters = editbox:GetMaxLetters()
+ editbox:SetMaxBytes(0)
+ editbox:SetMaxLetters(0)
+
+ editbox.GetText = newGetText
+ editbox.SetText = newSetText
+
+ hookHandler(editbox, "OnTextChanged", textChangedHook)
+ hookHandler(editbox, "OnTabPressed", tabPressedHook)
+ hookHandler(editbox, "OnUpdate", onUpdateHook)
+
+ lib.indentEditbox(editbox)
+ end
+
+ -- Deprecated function
+ lib.addSmartCode = lib.enable
+
+ function lib.disable(editbox)
+ if not enabled[editbox] then
+ return
+ end
+ enabled[editbox] = nil
+
+ -- revert settings for max bytes / letters
+ editbox:SetMaxBytes(editbox.oldMaxBytes)
+ editbox:SetMaxLetters(editbox.oldMaxLetters)
+
+ -- try a real unhooking, if possible
+ if editbox:GetScript("OnTextChanged") == textChangedHook then
+ editbox:SetScript("OnTextChanged", editbox.faiap_old_OnTextChanged)
+ editbox.faiap_old_OnTextChanged = nil
+ end
+
+ if editbox:GetScript("OnTabPressed") == tabPressedHook then
+ editbox:SetScript("OnTabPressed", editbox.faiap_old_OnTabPressed)
+ editbox.faiap_old_OnTabPressed = nil
+ end
+
+ if editbox:GetScript("OnUpdate") == onUpdateHook then
+ editbox:SetScript("OnUpdate", editbox.faiap_old_OnUpdate)
+ editbox.faiap_old_OnUpdate = nil
+ end
+
+ editbox.GetText = nil
+ editbox.SetText = nil
+
+ -- change the text back to unformatted
+ editbox:SetText(newGetText(editbox))
+
+ -- clear caches
+ editboxIndentCache[editbox] = nil
+ decodeCache[editbox] = nil
+ editboxStringCache[editbox] = nil
+ editboxNumLinesCache[editbox] = nil
+ end
+
+ defaultColorTable = {}
+ lib.defaultColorTable = defaultColorTable
+ defaultColorTable[tokens.TOKEN_SPECIAL] = "|c00ff99ff"
+ defaultColorTable[tokens.TOKEN_KEYWORD] = "|c006666ff"
+ defaultColorTable[tokens.TOKEN_COMMENT_SHORT] = "|c00999999"
+ defaultColorTable[tokens.TOKEN_COMMENT_LONG] = "|c00999999"
+
+ local stringColor = "|c00ffff77"
+ defaultColorTable[tokens.TOKEN_STRING] = stringColor
+ defaultColorTable[".."] = stringColor
+
+ local tableColor = "|c00ff9900"
+ defaultColorTable["..."] = tableColor
+ defaultColorTable["{"] = tableColor
+ defaultColorTable["}"] = tableColor
+ defaultColorTable["["] = tableColor
+ defaultColorTable["]"] = tableColor
+
+ local arithmeticColor = "|c0033ff55"
+ defaultColorTable[tokens.TOKEN_NUMBER] = arithmeticColor
+ defaultColorTable["+"] = arithmeticColor
+ defaultColorTable["-"] = arithmeticColor
+ defaultColorTable["/"] = arithmeticColor
+ defaultColorTable["*"] = arithmeticColor
+
+ local logicColor1 = "|c0055ff88"
+ defaultColorTable["=="] = logicColor1
+ defaultColorTable["<"] = logicColor1
+ defaultColorTable["<="] = logicColor1
+ defaultColorTable[">"] = logicColor1
+ defaultColorTable[">="] = logicColor1
+ defaultColorTable["~="] = logicColor1
+
+ local logicColor2 = "|c0088ffbb"
+ defaultColorTable["and"] = logicColor2
+ defaultColorTable["or"] = logicColor2
+ defaultColorTable["not"] = logicColor2
+
+ defaultColorTable[0] = "|r"
end
-- just for testing
--[[
function testTokenizer()
- local str = ""
- for line in io.lines("indent.lua") do
- str = str .. line .. "\n"
+ local str = ""
+ for line in io.lines("indent.lua") do
+ str = str .. line .. "\n"
+ end
+
+ local pos = 1
+
+ while true do
+ local tokenType, nextPos = nextToken(str, pos)
+
+ if not tokenType then
+ break
end
-
- local pos = 1
-
- while true do
- local tokenType, nextPos = nextToken(str, pos)
-
- if not tokenType then
- break
- end
-
- if true or tokenType ~= tokens.TOKEN_WHITESPACE and tokenType ~= tokens.TOKEN_LINEBREAK then
- print(stringformat("Found token %d (%d-%d): (%s)", tokenType, pos, nextPos - 1, stringsub(str, pos, nextPos - 1)))
- end
-
- if tokenType == tokens.TOKEN_UNKNOWN then
- print("unknown token!")
- break
- end
-
- pos = nextPos
+
+ if true or tokenType ~= tokens.TOKEN_WHITESPACE and tokenType ~= tokens.TOKEN_LINEBREAK then
+ print(stringformat("Found token %d (%d-%d): (%s)", tokenType, pos, nextPos - 1, stringsub(str, pos, nextPos - 1)))
end
+
+ if tokenType == tokens.TOKEN_UNKNOWN then
+ print("unknown token!")
+ break
+ end
+
+ pos = nextPos
+ end
end
function testIndenter(i)
- local lib = IndentationLib
- local str = ""
- for line in io.lines("test.lua") do
- str = str .. line .. "\n"
- end
-
- local colorTable = lib.defaultColorTable
- print(lib.indentCode(str, 4, colorTable, i))
+ local lib = IndentationLib
+ local str = ""
+ for line in io.lines("test.lua") do
+ str = str .. line .. "\n"
+ end
+
+ local colorTable = lib.defaultColorTable
+ print(lib.indentCode(str, 4, colorTable, i))
end
-
+
testIndenter()