Skip to content

Commit

Permalink
Util - MakeEnumType
Browse files Browse the repository at this point in the history
Save the DisplayName field for MakeEnumType so it can be read in the help command
  • Loading branch information
SignalManSteve committed Jun 27, 2024
1 parent 8d402d2 commit f858ee6
Showing 1 changed file with 56 additions and 135 deletions.
191 changes: 56 additions & 135 deletions Cmdr/Shared/Util.lua
Original file line number Diff line number Diff line change
@@ -1,20 +1,9 @@
local TextService = game:GetService("TextService")

--[=[
@class Util
Cmdr utilities module.
:::info Beta
This page is incomplete and some functions are missing. You might want to refer to [the current documentation](https://eryn.io/Cmdr/api/Util.html).
:::
]=]
local Util = {}

--[=[
Takes an array and flips its values into dictionary keys with value of true.
]=]
function Util.MakeDictionary(array: { any }): { [any]: true }
--- Takes an array and flips its values into dictionary keys with value of true.
function Util.MakeDictionary(array)
local dictionary = {}

for i = 1, #array do
Expand All @@ -24,10 +13,8 @@ function Util.MakeDictionary(array: { any }): { [any]: true }
return dictionary
end

--[=[
Takes a dictionary and returns its keys.
]=]
function Util.DictionaryKeys(dict: { [any]: any }): { any }
--- Takes a dictionary and returns its keys.
function Util.DictionaryKeys(dict)
local keys = {}

for key in pairs(dict) do
Expand All @@ -48,15 +35,11 @@ local function transformInstanceSet(instances)
return names, instances
end

--[=[
Returns a function that is a fuzzy finder for the specified set or container.
Can pass an array of strings, array of instances, array of EnumItems,
array of dictionaries with a Name key or an instance (in which case its children will be used).
Exact matches will be inserted in the front of the resulting array.
]=]
function Util.MakeFuzzyFinder(setOrContainer: any): (string, boolean?, boolean?) -> string
--- Returns a function that is a fuzzy finder for the specified set or container.
-- Can pass an array of strings, array of instances, array of EnumItems,
-- array of dictionaries with a Name key or an instance (in which case its children will be used)
-- Exact matches will be inserted in the front of the resulting array
function Util.MakeFuzzyFinder(setOrContainer)
local names
local instances = {}

Expand All @@ -76,16 +59,16 @@ function Util.MakeFuzzyFinder(setOrContainer: any): (string, boolean?, boolean?)
elseif type(setOrContainer[1]) == "string" then
names = setOrContainer
elseif setOrContainer[1] ~= nil then
error("[Cmdr] MakeFuzzyFinder only accepts tables of instances or strings.")
error("MakeFuzzyFinder only accepts tables of instances or strings.")
else
names = {}
end
else
error("[Cmdr] MakeFuzzyFinder only accepts a table, Enum, or Instance.")
error("MakeFuzzyFinder only accepts a table, Enum, or Instance.")
end

-- Searches the set (checking exact matches first)
return function(text: string, returnFirst: boolean?, matchStart: boolean?)
return function(text, returnFirst)
local results = {}

for i, name in pairs(names) do
Expand All @@ -99,10 +82,6 @@ function Util.MakeFuzzyFinder(setOrContainer: any): (string, boolean?, boolean?)
else
table.insert(results, 1, value)
end
elseif matchStart then
if name:lower():sub(1, #text) == text:lower() then
results[#results + 1] = value
end
elseif name:lower():find(text:lower(), 1, true) then
results[#results + 1] = value
end
Expand All @@ -116,12 +95,8 @@ function Util.MakeFuzzyFinder(setOrContainer: any): (string, boolean?, boolean?)
end
end

--[=[
Takes an array of instances (or anything with a Name property) and maps them into an array of their names.
If no Name property is present, then tostring will be called instead.
]=]
function Util.GetNames(instances: any): { string }
--- Takes an array of instances and returns an array of those instances' names.
function Util.GetNames(instances)
local names = {}

for i = 1, #instances do
Expand All @@ -131,30 +106,26 @@ function Util.GetNames(instances: any): { string }
return names
end

--[=[
Splits a string using a simple separator (no quote parsing)
]=]
function Util.SplitStringSimple(input: string, sep: string?): { string }
--- Splits a string using a simple separator (no quote parsing)
function Util.SplitStringSimple(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t = {}
local i = 1
for str in string.gmatch(input, "([^" .. (sep :: string) .. "]+)") do
for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
t[i] = str
i = i + 1
end
return t
end

local function charCode(n)
return utf8.char(tonumber(n, 16) :: number)
return utf8.char(tonumber(n, 16))
end

--[=[
Parses escape sequences into their fully qualified characters
]=]
function Util.ParseEscapeSequences(text: string): string
--- Parses escape sequences into their fully qualified characters
function Util.ParseEscapeSequences(text)
return text:gsub("\\(.)", {
t = "\t",
n = "\n",
Expand All @@ -163,10 +134,7 @@ function Util.ParseEscapeSequences(text: string): string
:gsub("\\x(%x%x)", charCode)
end

--[=[
No description.
]=]
function Util.EncodeEscapedOperator(text: string, op: string): string
function Util.EncodeEscapedOperator(text, op)
local first = op:sub(1, 1)
local escapedOp = op:gsub(".", "%%%1")
local escapedFirst = "%" .. first
Expand All @@ -179,10 +147,7 @@ function Util.EncodeEscapedOperator(text: string, op: string): string
end

local OPERATORS = { "&&", "||", ";" }
--[=[
No description.
]=]
function Util.EncodeEscapedOperators(text: string): string
function Util.EncodeEscapedOperators(text)
for _, operator in ipairs(OPERATORS) do
text = Util.EncodeEscapedOperator(text, operator)
end
Expand All @@ -203,10 +168,8 @@ local function decodeControlChars(text)
return (text:gsub("___!CMDR_ESCAPE!___", "\\"):gsub("___!CMDR_QUOTE!___", '"'):gsub("___!CMDR_NL!___", "\n"))
end

--[=[
Splits a string by space but taking into account quoted sequences which will be treated as a single argument.
]=]
function Util.SplitString(text: string, max: number): { string }
--- Splits a string by space but taking into account quoted sequences which will be treated as a single argument.
function Util.SplitString(text, max)
text = encodeControlChars(text)
max = max or math.huge
local t = {}
Expand Down Expand Up @@ -236,10 +199,9 @@ function Util.SplitString(text: string, max: number): { string }
return t
end

--[=[
Takes an array of arguments and a max value. Any indicies past the max value will be appended to the last valid argument.
]=]
function Util.MashExcessArguments(arguments: { string }, max: number): { string }
--- Takes an array of arguments and a max value.
-- Any indicies past the max value will be appended to the last valid argument.
function Util.MashExcessArguments(arguments, max)
local t = {}
for i = 1, #arguments do
if i > max then
Expand All @@ -251,29 +213,23 @@ function Util.MashExcessArguments(arguments: { string }, max: number): { string
return t
end

--[=[
Trims whitespace from both sides of a string.
]=]
function Util.TrimString(str: string): string
--- Trims whitespace from both sides of a string.
function Util.TrimString(str)
local _, from = string.find(str, "^%s*")
-- trim the string in two steps to prevent quadratic backtracking when no "%S" match is found
return from == #str and "" or string.match(str, ".*%S", from + 1)
end

--[=[
Returns the text bounds size based on given text, label (from which properties will be pulled), and optional Vector2 container size.
]=]
function Util.GetTextSize(text: string, label: TextLabel, size: Vector2?): Vector2
--- Returns the text bounds size based on given text, label (from which properties will be pulled), and optional Vector2 container size.
function Util.GetTextSize(text, label, size)
return TextService:GetTextSize(text, label.TextSize, label.Font, size or Vector2.new(label.AbsoluteSize.X, 0))
end

--[=[
Makes an Enum type.
@return TypeDefinition
]=]
function Util.MakeEnumType(name: string, values: any)
--- Makes an Enum type.
function Util.MakeEnumType(name, values)
local findValue = Util.MakeFuzzyFinder(values)
return {
DisplayName = name,
Validate = function(text)
return findValue(text, true) ~= nil, ("Value %q is not a valid %s."):format(text, name)
end,
Expand All @@ -287,10 +243,8 @@ function Util.MakeEnumType(name: string, values: any)
}
end

--[=[
Parses a prefixed union type argument (such as %Team)
]=]
function Util.ParsePrefixedUnionType(typeValue: string, rawValue: string): (string?, string?, string?)
--- Parses a prefixed union type argument (such as %Team)
function Util.ParsePrefixedUnionType(typeValue, rawValue)
local split = Util.SplitStringSimple(typeValue)

-- Check prefixes in order from longest to shortest
Expand All @@ -313,15 +267,9 @@ function Util.ParsePrefixedUnionType(typeValue: string, rawValue: string): (stri
return t.type, rawValue:sub(#t.prefix + 1), t.prefix
end
end
return
end

--[=[
Creates a listable type from a singular type
@param type TypeDefinition
@param override table
@return TypeDefinition
]=]
--- Creates a listable type from a singlular type
function Util.MakeListableType(type, override)
local listableType = {
Listable = true,
Expand Down Expand Up @@ -353,12 +301,7 @@ local function decodeCommandEscape(text)
return (text:gsub("___!CMDR_DOLLAR!___", "$"))
end

--[=[
Creates a listable type from a singular type
@param dispatcher Dispatcher
@return string? -- output from Dispatcher:EvaluateAndRun as a string
]=]
function Util.RunCommandString(dispatcher, commandString: string): string?
function Util.RunCommandString(dispatcher, commandString)
commandString = Util.ParseEscapeSequences(commandString)
commandString = Util.EncodeEscapedOperators(commandString)

Expand All @@ -375,15 +318,10 @@ function Util.RunCommandString(dispatcher, commandString: string): string?
return output
end
end
return
end

--[=[
Runs embedded commands and replaces them
@param dispatcher Dispatcher
@param str string
]=]
function Util.RunEmbeddedCommands(dispatcher, str): string
--- Runs embedded commands and replaces them
function Util.RunEmbeddedCommands(dispatcher, str)
str = encodeCommandEscape(str)

local results = {}
Expand All @@ -409,12 +347,9 @@ function Util.RunEmbeddedCommands(dispatcher, str): string
return decodeCommandEscape(str:gsub("$(%b{})", results))
end

--[=[
Replaces arguments in the format $1, $2, $something with whatever the given function returns for it.
@param str string
@param replace string
]=]
function Util.SubstituteArgs(str, replace): string
--- Replaces arguments in the format $1, $2, $something with whatever the
-- given function returns for it.
function Util.SubstituteArgs(str, replace)
str = encodeCommandEscape(str)
-- Convert numerical keys to strings
if type(replace) == "table" then
Expand All @@ -430,11 +365,8 @@ function Util.SubstituteArgs(str, replace): string
return decodeCommandEscape(str:gsub("($%d+)%b{}", "%1"):gsub("$(%w+)", replace))
end

--[=[
Creates an alias command, should only be used on the client.
@return CommandDefinition
]=]
function Util.MakeAliasCommand(name: string, commandString: string)
--- Creates an alias command
function Util.MakeAliasCommand(name, commandString)
local commandName, commandDescription = unpack(name:split("|"))
local args = {}

Expand Down Expand Up @@ -473,26 +405,19 @@ function Util.MakeAliasCommand(name: string, commandString: string)
Description = `<Alias> {commandDescription or commandString}`,
Group = "UserAlias",
Args = args,
ClientRun = function(context)
Run = function(context)
return Util.RunCommandString(context.Dispatcher, Util.SubstituteArgs(commandString, context.RawArguments))
end,
}
end

--[=[
Makes a type that contains a sequence, e.g. Vector3 or Color3
For options, one of Constructor or Parse is required
@param options { Parse: function?, Constructor: function?, TransformEach: function?, ValidateEach: function?, Prefixes: {string}?, Length: number? }
@return ArgumentDefinition
]=]
--- Makes a type that contains a sequence, e.g. Vector3 or Color3
function Util.MakeSequenceType(options)
options = options or {}

assert(
options.Parse ~= nil or options.Constructor ~= nil,
"[Cmdr] MakeSequenceType: Must provide one of: Constructor, Parse"
"MakeSequenceType: Must provide one of: Constructor, Parse"
)

options.TransformEach = options.TransformEach or function(...)
Expand Down Expand Up @@ -534,21 +459,17 @@ function Util.MakeSequenceType(options)
}
end

--[=[
Splits a string by a single delimeter chosen from the given set. The first matching delimeter from the set becomes the split character.
]=]
function Util.SplitPrioritizedDelimeter(text: string, delimiters: { string }): { string }?
for i, delimeter in ipairs(delimiters) do
if text:find(delimeter) or i == #delimiters then
--- Splits a string by a single delimeter chosen from the given set.
-- The first matching delimeter from the set becomes the split character.
function Util.SplitPrioritizedDelimeter(text, delimeters)
for i, delimeter in ipairs(delimeters) do
if text:find(delimeter) or i == #delimeters then
return Util.SplitStringSimple(text, delimeter)
end
end
return
end

-- TODO: Continue documentation from here down

-- Maps values of an array through a callback and returns an array of mapped values.
--- Maps values of an array through a callback and returns an array of mapped values
function Util.Map(array, callback)
local results = {}

Expand All @@ -559,7 +480,7 @@ function Util.Map(array, callback)
return results
end

-- Maps arguments #2-n through callback and returns values as tuple
--- Maps arguments #2-n through callback and returns values as tuple
function Util.Each(callback, ...)
local results = {}
for i, value in ipairs({ ... }) do
Expand All @@ -568,7 +489,7 @@ function Util.Each(callback, ...)
return unpack(results)
end

-- Emulates tabstops with spaces
--- Emulates tabstops with spaces
function Util.EmulateTabstops(text, tabWidth)
local column = 0
local textLength = #text
Expand Down

0 comments on commit f858ee6

Please sign in to comment.