diff --git a/main.go b/main.go index e8b0b10..a189cf0 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( _ "embed" "errors" "os" + "path/filepath" "sort" "sync" "sync/atomic" @@ -64,6 +65,20 @@ Features: CreateExampleConfig() return } + metaFlag, _ := cmd.Flags().GetBool("meta") + if metaFlag { + cwd, err := os.Getwd() + if err != nil { + mainLogger.Error("Failed to get current directory: %v", err) + os.Exit(1) + } + metaPath := filepath.Join(cwd, "meta.lua") + if err := processor.GenerateMetaFile(metaPath); err != nil { + mainLogger.Error("Failed to generate meta.lua: %v", err) + os.Exit(1) + } + return + } if len(args) == 0 { cmd.Usage() return @@ -81,6 +96,7 @@ Features: rootCmd.Flags().Bool("json", false, "Enable JSON mode for processing JSON files") rootCmd.Flags().BoolP("conv", "c", false, "Convert YAML files to TOML format") rootCmd.Flags().BoolP("example", "e", false, "Generate example_cook.toml and exit") + rootCmd.Flags().BoolP("meta", "m", false, "Generate meta.lua file for LuaLS autocomplete and exit") // Set up examples in the help text rootCmd.SetUsageTemplate(`Usage:{{if .Runnable}} diff --git a/processor/meta.go b/processor/meta.go new file mode 100644 index 0000000..bbf5042 --- /dev/null +++ b/processor/meta.go @@ -0,0 +1,29 @@ +package processor + +import ( + _ "embed" + "fmt" + "os" + + logger "git.site.quack-lab.dev/dave/cylogger" +) + +//go:embed meta.lua +var metaFileContent string + +var metaLogger = logger.Default.WithPrefix("meta") + +// GenerateMetaFile generates meta.lua with function signatures for LuaLS autocomplete +func GenerateMetaFile(outputPath string) error { + metaLogger.Info("Generating meta.lua file for LuaLS autocomplete") + + // Write the embedded meta file + err := os.WriteFile(outputPath, []byte(metaFileContent), 0644) + if err != nil { + metaLogger.Error("Failed to write meta.lua: %v", err) + return fmt.Errorf("failed to write meta.lua: %w", err) + } + + metaLogger.Info("Successfully generated meta.lua at %q", outputPath) + return nil +} diff --git a/processor/meta.lua b/processor/meta.lua new file mode 100644 index 0000000..c68b5ef --- /dev/null +++ b/processor/meta.lua @@ -0,0 +1,245 @@ +---@meta + +---@class ParserOptions +---@field delimiter string? The field delimiter (default: ","). +---@field hasheader boolean? If true, first non-comment row is treated as headers (default: false). +---@field hascomments boolean? If true, lines starting with '#' are skipped (default: false). + +---@class XMLElement +---@field _tag string The XML tag name +---@field _attr {[string]: string}? XML attributes as key-value pairs +---@field _text string? Text content of the element +---@field _children XMLElement[]? Child elements + +---@class JSONNode +---@field [string] string | number | boolean | nil | JSONNode | JSONArray JSON object fields +---@alias JSONArray (string | number | boolean | nil | JSONNode)[] + +---@class CSVRow +---@field [integer] string Numeric indices for field access +---@field Headers string[]? Header row if hasheader was true + +--- Returns the minimum of two numbers +---@param a number First number +---@param b number Second number +---@return number #Minimum value +function min(a, b) end + +--- Returns the maximum of two numbers +---@param a number First number +---@param b number Second number +---@return number #Maximum value +function max(a, b) end + +--- Rounds a number to n decimal places +---@param x number Number to round +---@param n number? Number of decimal places (default: 0) +---@return number #Rounded number +function round(x, n) end + +--- Returns the floor of a number +---@param x number Number to floor +---@return number #Floored number +function floor(x) end + +--- Returns the ceiling of a number +---@param x number Number to ceil +---@return number #Ceiled number +function ceil(x) end + +--- Converts string to uppercase +---@param s string String to convert +---@return string #Uppercase string +function upper(s) end + +--- Converts string to lowercase +---@param s string String to convert +---@return string #Lowercase string +function lower(s) end + +--- Formats a string using Lua string.format +---@param s string Format string +---@param ... any Values to format +---@return string #Formatted string +function format(s, ...) end + +--- Removes leading and trailing whitespace from string +---@param s string String to trim +---@return string #Trimmed string +function trim(s) end + +--- Splits a string by separator +---@param inputstr string String to split +---@param sep string? Separator pattern (default: whitespace) +---@return string[] #Array of string parts +function strsplit(inputstr, sep) end + +--- Prints table structure recursively +---@param table {[any]: any} Table to dump +---@param depth number? Current depth (default: 0) +function dump(table, depth) end + +--- Validates options against a set of valid option keys. +---@param options ParserOptions? The options table to validate +function areOptionsValid(options) end + +--- Parses CSV text into rows and fields using a minimal RFC 4180 state machine. +--- Requirements/assumptions:
+--- Input is a single string containing the entire CSV content.
+--- Field separators are specified by delimiter option (default: comma).
+--- Newlines between rows may be "\n" or "\r\n". "\r\n" is treated as one line break.
+--- Fields may be quoted with double quotes (").
+--- Inside quoted fields, doubled quotes ("") represent a literal quote character.
+--- No backslash escaping is supported (not part of RFC 4180).
+--- Newlines inside quoted fields are preserved as part of the field.
+--- Leading/trailing spaces are preserved; no trimming is performed.
+--- Empty fields and empty rows are preserved.
+--- The final row is emitted even if the text does not end with a newline.
+--- Lines starting with '#' (after optional leading whitespace) are treated as comments and skipped if hascomments is true.
+---@param csv string The CSV text to parse. +---@param options ParserOptions? Options for the parser +---@return CSVRow[] #A table (array) of rows; each row is a table with numeric indices and optionally header-named keys. +function fromCSV(csv, options) end + +--- Converts a table of rows back to CSV text format (RFC 4180 compliant).
+--- Requirements:
+--- Input is a table (array) of rows, where each row is a table (array) of field values.
+--- Field values are converted to strings using tostring().
+--- Fields are quoted if they contain the delimiter, newlines, or double quotes.
+--- Double quotes inside quoted fields are doubled ("").
+--- Fields are joined with the specified delimiter; rows are joined with newlines.
+--- If includeHeaders is true and rows have a Headers field, headers are included as the first row.
+---@param rows CSVRow[] Array of rows, where each row is an array of field values. +---@param options ParserOptions? Options for the parser +---@return string #CSV-formatted text +function toCSV(rows, options) end + +--- Converts string to number, returns 0 if invalid +---@param str string String to convert +---@return number #Numeric value or 0 +function num(str) end + +--- Converts number to string +---@param num number Number to convert +---@return string #String representation +function str(num) end + +--- Checks if string is numeric +---@param str string String to check +---@return boolean #True if string is numeric +function is_number(str) end + +--- Checks if table is a sequential array (1-indexed with no gaps) +---@param t {[integer]: any} Table to check +---@return boolean #True if table is an array +function isArray(t) end + +--- Find all elements with a specific tag name (recursive search) +---@param root XMLElement The root XML element (with _tag, _attr, _children fields) +---@param tagName string The tag name to search for +---@return XMLElement[] #Array of matching elements +function findElements(root, tagName) end + +--- Visit all elements recursively and call a function on each +---@param root XMLElement The root XML element +---@param callback fun(element: XMLElement, depth: number, path: string) Function to call with each element +function visitElements(root, callback) end + +--- Get numeric value from XML element attribute +---@param element XMLElement XML element with _attr field +---@param attrName string Attribute name +---@return number? #The numeric value or nil if not found/not numeric +function getNumAttr(element, attrName) end + +--- Set numeric value to XML element attribute +---@param element XMLElement XML element with _attr field +---@param attrName string Attribute name +---@param value number Numeric value to set +function setNumAttr(element, attrName, value) end + +--- Modify numeric attribute by applying a function +---@param element XMLElement XML element +---@param attrName string Attribute name +---@param func fun(currentValue: number): number Function that takes current value and returns new value +---@return boolean #True if modification was made +function modifyNumAttr(element, attrName, func) end + +--- Find all elements matching a predicate function +---@param root XMLElement The root XML element +---@param predicate fun(element: XMLElement): boolean Function that takes element and returns true/false +---@return XMLElement[] #Array of matching elements +function filterElements(root, predicate) end + +--- Get text content of an element +---@param element XMLElement XML element +---@return string? #The text content or nil +function getText(element) end + +--- Set text content of an element +---@param element XMLElement XML element +---@param text string Text content to set +function setText(element, text) end + +--- Check if element has an attribute +---@param element XMLElement XML element +---@param attrName string Attribute name +---@return boolean #True if attribute exists +function hasAttr(element, attrName) end + +--- Get attribute value as string +---@param element XMLElement XML element +---@param attrName string Attribute name +---@return string? #The attribute value or nil +function getAttr(element, attrName) end + +--- Set attribute value +---@param element XMLElement XML element +---@param attrName string Attribute name +---@param value string | number | boolean Value to set (will be converted to string) +function setAttr(element, attrName, value) end + +--- Find first element with a specific tag name (searches direct children only) +---@param parent XMLElement The parent XML element +---@param tagName string The tag name to search for +---@return XMLElement? #The first matching element or nil +function findFirstElement(parent, tagName) end + +--- Add a child element to a parent +---@param parent XMLElement The parent XML element +---@param child XMLElement The child element to add +function addChild(parent, child) end + +--- Remove all children with a specific tag name +---@param parent XMLElement The parent XML element +---@param tagName string The tag name to remove +---@return number #Count of removed children +function removeChildren(parent, tagName) end + +--- Get all direct children with a specific tag name +---@param parent XMLElement The parent XML element +---@param tagName string The tag name to search for +---@return XMLElement[] #Array of matching children +function getChildren(parent, tagName) end + +--- Count children with a specific tag name +---@param parent XMLElement The parent XML element +---@param tagName string The tag name to count +---@return number #Count of matching children +function countChildren(parent, tagName) end + +--- Recursively visit all values in a JSON structure +---@param data JSONNode | JSONArray JSON data (nested tables) +---@param callback fun(value: string | number | boolean | nil | JSONNode | JSONArray, key: string?, parent: JSONNode?): nil Function called with (value, key, parent) +function visitJSON(data, callback) end + +--- Find all values in JSON matching a predicate +---@param data JSONNode | JSONArray JSON data +---@param predicate fun(value: string | number | boolean | nil | JSONNode | JSONArray, key: string?, parent: JSONNode?): boolean Function that takes (value, key, parent) and returns true/false +---@return (string | number | boolean | nil | JSONNode | JSONArray)[] #Array of matching values +function findInJSON(data, predicate) end + +--- Modify all numeric values in JSON matching a condition +---@param data JSONNode | JSONArray JSON data +---@param predicate fun(value: string | number | boolean | nil | JSONNode | JSONArray, key: string?, parent: JSONNode?): boolean Function that takes (value, key, parent) and returns true/false +---@param modifier fun(currentValue: number): number Function that takes current value and returns new value +function modifyJSONNumbers(data, predicate, modifier) end