You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
280 lines
8.6 KiB
280 lines
8.6 KiB
-- ------------------------------------------------------------------------------ --
|
|
-- TradeSkillMaster --
|
|
-- https://tradeskillmaster.com --
|
|
-- All Rights Reserved - Detailed license information included with addon. --
|
|
-- ------------------------------------------------------------------------------ --
|
|
|
|
local TSM = select(2, ...) ---@type TSM
|
|
local SyntaxHelpers = TSM.Init("UI.SyntaxHelpers") ---@class UI.SyntaxHelpers
|
|
local CustomPrice = TSM.Include("Service.CustomPrice")
|
|
local Theme = TSM.Include("Util.Theme")
|
|
local Money = TSM.Include("Util.Money")
|
|
local CustomString = TSM.Include("Util.CustomString")
|
|
local private = {
|
|
result = {},
|
|
resultLen = nil,
|
|
pos = nil,
|
|
cursor = nil,
|
|
newCursor = nil,
|
|
prevTokenLength = nil,
|
|
prevTokenColored = nil,
|
|
tokenList = CustomString.CreateTokenList(),
|
|
tokenListRow = nil,
|
|
}
|
|
local NEWLINE_CHAR = "\n"
|
|
local INDENT_STR = " "
|
|
local TEXT_COLOR_SUFFIX = "|r"
|
|
local DIVIDER_COLOR = "GROUP_ONE"
|
|
local MATH_COLOR = "GROUP_FOUR"
|
|
local FUNCTION_COLOR = "GROUP_THREE"
|
|
local SOURCE_COLOR = "GROUP_TWO"
|
|
local CUSTOM_SOURCE_COLOR = "GROUP_FIVE"
|
|
local NUMBER_COLOR = "TEXT"
|
|
local UNKNOWN_COLOR = "FEEDBACK_RED"
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Module Functions
|
|
-- ============================================================================
|
|
|
|
function SyntaxHelpers.StripWhitespace(text)
|
|
text = gsub(text, "[\n\r ]", "")
|
|
return text
|
|
end
|
|
|
|
function SyntaxHelpers.StripColors(value)
|
|
value = gsub(value, "|c%x%x%x%x%x%x%x%x", "")
|
|
value = gsub(value, TEXT_COLOR_SUFFIX, "")
|
|
return value
|
|
end
|
|
|
|
function SyntaxHelpers.SetAutoIndent(text)
|
|
assert(#private.result == 0)
|
|
local writeIndex = 1
|
|
for readIndex = 1, #text do
|
|
local char = strsub(text, readIndex, readIndex)
|
|
local nextChar = strsub(text, readIndex + 1, readIndex + 1)
|
|
local shouldAddNewline = nil
|
|
if (char == "(" or char == ",") and nextChar ~= "\n" then
|
|
shouldAddNewline = true
|
|
elseif char ~= "\n" and char ~= " " and nextChar == ")" then
|
|
shouldAddNewline = true
|
|
elseif char == ")" and nextChar ~= "\n" and nextChar ~= "," then
|
|
shouldAddNewline = true
|
|
else
|
|
shouldAddNewline = false
|
|
end
|
|
if shouldAddNewline then
|
|
tinsert(private.result, strsub(text, writeIndex, readIndex))
|
|
tinsert(private.result, NEWLINE_CHAR)
|
|
writeIndex = readIndex + 1
|
|
end
|
|
end
|
|
tinsert(private.result, strsub(text, writeIndex))
|
|
local result = table.concat(private.result)
|
|
wipe(private.result)
|
|
return result
|
|
end
|
|
|
|
function SyntaxHelpers.SetSyntaxColor(text, cursor)
|
|
private.PrepareContext(private.StripColorsAtPos(text, cursor))
|
|
private.SetSyntaxColor()
|
|
return private.ClearContextAndReturnResult()
|
|
end
|
|
|
|
function SyntaxHelpers.SetIndent(text, cursor)
|
|
private.PrepareContext(private.StripColorsAtPos(text, cursor))
|
|
private.SetIndent()
|
|
return private.ClearContextAndReturnResult()
|
|
end
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- Private Helper Functions
|
|
-- ============================================================================
|
|
|
|
function private.PrepareContext(text, cursor)
|
|
assert(#private.result == 0)
|
|
assert(private.tokenList:GetNumRows() == 0)
|
|
private.pos = 1
|
|
private.cursor = cursor
|
|
private.newCursor = nil
|
|
private.prevTokenLength = 0
|
|
private.prevTokenColored = false
|
|
private.resultLen = 0
|
|
CustomString.PopulateTokenList(private.tokenList, strlower(text))
|
|
private.tokenListRow = 1
|
|
end
|
|
|
|
function private.ClearContextAndReturnResult()
|
|
local result = table.concat(private.result)
|
|
wipe(private.result)
|
|
private.tokenList:Wipe()
|
|
return result, private.newCursor
|
|
end
|
|
|
|
function private.SetSyntaxColor()
|
|
while true do
|
|
private.UpdateNewCursor()
|
|
local tokenType, tokenStr = private.NextToken()
|
|
if not tokenType then
|
|
break
|
|
end
|
|
private.prevTokenColored = false
|
|
if tokenType == CustomString.TOKEN_TYPE.NEWLINE or tokenType == CustomString.TOKEN_TYPE.WHITESPACE then
|
|
private.InsertResult(tokenStr)
|
|
else
|
|
private.InsertColoredToken(tokenType, tokenStr)
|
|
end
|
|
private.prevTokenLength = #tokenStr
|
|
end
|
|
end
|
|
|
|
function private.SetIndent()
|
|
local newCursorPosFinalized = false
|
|
local level = 0
|
|
local hitNonWhitespace = false
|
|
local hitIndentRight = false
|
|
local preIndent = 0
|
|
local postIndent = 0
|
|
local bufferStartIndex = 1
|
|
while true do
|
|
private.UpdateNewCursor()
|
|
private.prevTokenColored = false
|
|
private.prevTokenLength = 0
|
|
local tokenType, tokenStr = private.NextToken()
|
|
if not tokenType or tokenType == CustomString.TOKEN_TYPE.NEWLINE then
|
|
level = max(level + preIndent, 0)
|
|
local indentStr = strrep(INDENT_STR, level)
|
|
private.InsertResult(indentStr, bufferStartIndex)
|
|
if private.newCursor and not newCursorPosFinalized then
|
|
private.newCursor = private.newCursor + #indentStr
|
|
newCursorPosFinalized = true
|
|
end
|
|
if not tokenType then
|
|
break
|
|
end
|
|
private.InsertResult(tokenStr)
|
|
bufferStartIndex = #private.result + 1
|
|
level = max(level + postIndent, 0)
|
|
hitNonWhitespace = false
|
|
hitIndentRight = false
|
|
preIndent = 0
|
|
postIndent = 0
|
|
elseif tokenType ~= CustomString.TOKEN_TYPE.WHITESPACE then
|
|
hitNonWhitespace = true
|
|
private.prevTokenLength = #tokenStr
|
|
if tokenType == CustomString.TOKEN_TYPE.LEFT_PAREN then
|
|
if hitIndentRight then
|
|
postIndent = postIndent + 1
|
|
else
|
|
hitIndentRight = true
|
|
postIndent = postIndent + 1
|
|
end
|
|
elseif tokenType == CustomString.TOKEN_TYPE.RIGHT_PAREN then
|
|
if hitIndentRight then
|
|
postIndent = postIndent - 1
|
|
else
|
|
preIndent = preIndent - 1
|
|
end
|
|
end
|
|
private.InsertColoredToken(tokenType, tokenStr)
|
|
elseif hitNonWhitespace then
|
|
private.InsertResult(tokenStr)
|
|
private.prevTokenLength = #tokenStr
|
|
end
|
|
end
|
|
end
|
|
|
|
function private.StripColorsAtPos(text, pos)
|
|
local left = SyntaxHelpers.StripColors(strsub(text, 1, pos))
|
|
local right = SyntaxHelpers.StripColors(strsub(text, pos + 1))
|
|
return left..right, #left + 1
|
|
end
|
|
|
|
function private.UpdateNewCursor()
|
|
if private.newCursor or private.pos < private.cursor then
|
|
return
|
|
end
|
|
private.newCursor = private.resultLen
|
|
if private.pos ~= private.cursor then
|
|
local diff = private.pos - private.cursor
|
|
if diff > private.prevTokenLength then
|
|
diff = private.prevTokenLength
|
|
end
|
|
if private.prevTokenColored then
|
|
diff = diff + #TEXT_COLOR_SUFFIX
|
|
end
|
|
private.newCursor = private.newCursor - diff
|
|
end
|
|
end
|
|
|
|
function private.NextToken()
|
|
if private.tokenListRow > private.tokenList:GetNumRows() then
|
|
return
|
|
end
|
|
local tokenType, tokenStr = private.tokenList:GetRow(private.tokenListRow)
|
|
private.tokenListRow = private.tokenListRow + 1
|
|
private.pos = private.pos + #tokenStr
|
|
return tokenType, tokenStr
|
|
end
|
|
|
|
function private.InsertColoredToken(tokenType, str)
|
|
local color = nil
|
|
if tokenType == CustomString.TOKEN_TYPE.NUMBER or tokenType == CustomString.TOKEN_TYPE.NEGATIVE_OPERATOR then
|
|
color = NUMBER_COLOR
|
|
elseif tokenType == CustomString.TOKEN_TYPE.MONEY then
|
|
color = NUMBER_COLOR
|
|
local symbol = strsub(str, -1)
|
|
local coloredSymbol = nil
|
|
if symbol == "g" then
|
|
coloredSymbol = Money.GetGoldText()
|
|
elseif symbol == "s" then
|
|
coloredSymbol = Money.GetSilverText()
|
|
elseif symbol == "c" then
|
|
coloredSymbol = Money.GetCopperText()
|
|
else
|
|
error("Unexpected currency symbol: "..symbol)
|
|
end
|
|
private.InsertResult(strsub(str, 1, -2))
|
|
private.InsertResult(coloredSymbol)
|
|
private.prevTokenColored = true
|
|
return
|
|
elseif tokenType == CustomString.TOKEN_TYPE.MATH_OPERATOR then
|
|
color = MATH_COLOR
|
|
elseif tokenType == CustomString.TOKEN_TYPE.LEFT_PAREN or tokenType == CustomString.TOKEN_TYPE.RIGHT_PAREN or tokenType == CustomString.TOKEN_TYPE.COMMA then
|
|
color = DIVIDER_COLOR
|
|
elseif tokenType == CustomString.TOKEN_TYPE.IDENTIFIER or tokenType == CustomString.TOKEN_TYPE.FUNCTION then
|
|
if CustomPrice.IsMathFunction(str) then
|
|
color = FUNCTION_COLOR
|
|
elseif CustomPrice.IsSource(str) then
|
|
color = SOURCE_COLOR
|
|
elseif CustomPrice.IsCustomSource(str) then
|
|
color = CUSTOM_SOURCE_COLOR
|
|
elseif strlower(str) == "baseitem" or strmatch(str, "^[ip]:%d+") then
|
|
color = CUSTOM_SOURCE_COLOR
|
|
else
|
|
color = UNKNOWN_COLOR
|
|
end
|
|
elseif tokenType == CustomString.TOKEN_TYPE.UNKNOWN then
|
|
color = UNKNOWN_COLOR
|
|
else
|
|
error("Unexpected token: "..tostring(tokenType))
|
|
end
|
|
private.prevTokenColored = true
|
|
local prefix = Theme.GetColor(color):GetTextColorPrefix()
|
|
private.InsertResult(prefix)
|
|
private.InsertResult(str)
|
|
private.InsertResult(TEXT_COLOR_SUFFIX)
|
|
end
|
|
|
|
function private.InsertResult(str, index)
|
|
if index then
|
|
tinsert(private.result, index, str)
|
|
else
|
|
tinsert(private.result, str)
|
|
end
|
|
private.resultLen = private.resultLen + #str
|
|
end
|
|
|