diff --git a/src/module.c b/src/module.c index e5afa952f..08ad0d138 100644 --- a/src/module.c +++ b/src/module.c @@ -1160,8 +1160,8 @@ int VM_IsChannelsPositionRequest(ValkeyModuleCtx *ctx) { * The following is an example of how it could be used: * * if (ValkeyModule_IsChannelsPositionRequest(ctx)) { - * ValkeyModule_ChannelAtPosWithFlags(ctx, 1, VALKEYMODULE_CMD_CHANNEL_SUBSCRIBE | - * VALKEYMODULE_CMD_CHANNEL_PATTERN); ValkeyModule_ChannelAtPosWithFlags(ctx, 1, VALKEYMODULE_CMD_CHANNEL_PUBLISH); + * ValkeyModule_ChannelAtPosWithFlags(ctx, 1, VALKEYMODULE_CMD_CHANNEL_SUBSCRIBE | VALKEYMODULE_CMD_CHANNEL_PATTERN); + * ValkeyModule_ChannelAtPosWithFlags(ctx, 1, VALKEYMODULE_CMD_CHANNEL_PUBLISH); * } * * Note: One usage of declaring channels is for evaluating ACL permissions. In this context, @@ -10940,7 +10940,7 @@ int moduleUnregisterFilters(ValkeyModule *module) { * * 1. Invocation by a client. * 2. Invocation through `ValkeyModule_Call()` by any module. - * 3. Invocation through Lua `redis.call()`. + * 3. Invocation through Lua `server.call()`. * 4. Replication of a command from a primary. * * The filter executes in a special filter context, which is different and more @@ -10980,8 +10980,9 @@ int moduleUnregisterFilters(ValkeyModule *module) { * If multiple filters are registered (by the same or different modules), they * are executed in the order of registration. */ -ValkeyModuleCommandFilter * -VM_RegisterCommandFilter(ValkeyModuleCtx *ctx, ValkeyModuleCommandFilterFunc callback, int flags) { +ValkeyModuleCommandFilter *VM_RegisterCommandFilter(ValkeyModuleCtx *ctx, + ValkeyModuleCommandFilterFunc callback, + int flags) { ValkeyModuleCommandFilter *filter = zmalloc(sizeof(*filter)); filter->module = ctx->module; filter->callback = callback; diff --git a/utils/generate-module-api-doc.rb b/utils/generate-module-api-doc.rb index 65fdeeaeb..5744bdb67 100755 --- a/utils/generate-module-api-doc.rb +++ b/utils/generate-module-api-doc.rb @@ -46,24 +46,75 @@ def linebreak_proto(proto, indent) if proto.bytesize <= 80 return proto end - parts = proto.split(/,\s*/); - if parts.length == 1 - return proto; - end - align_pos = proto.index("(") + 1; - align = " " * align_pos - result = parts.shift; - bracket_balance = 0; - parts.each{|part| - if bracket_balance == 0 - result += ",\n" + indent + align - else - result += ", " + + # Find the opening parenthesis for parameters + paren_pos = proto.index("(") + return proto if paren_pos.nil? + + # Split the prototype into parts: return_type function_name(params); + before_params = proto[0..paren_pos] + params_and_end = proto[paren_pos+1..-1] + + # Find the closing parenthesis (handling nested parentheses) + bracket_count = 0 + close_paren_pos = nil + params_and_end.each_char.with_index do |char, idx| + if char == '(' + bracket_count += 1 + elsif char == ')' + if bracket_count == 0 + close_paren_pos = idx + break + else + bracket_count -= 1 + end end - result += part - bracket_balance += part.count("(") - part.count(")") - } - return result; + end + + return proto if close_paren_pos.nil? + + params = params_and_end[0...close_paren_pos] + after_params = params_and_end[close_paren_pos..-1] + + # Split parameters on commas, but respect nested parentheses + param_parts = [] + current_param = "" + bracket_balance = 0 + + params.each_char do |char| + if char == '(' + bracket_balance += 1 + current_param += char + elsif char == ')' + bracket_balance -= 1 + current_param += char + elsif char == ',' && bracket_balance == 0 + param_parts << current_param.strip + current_param = "" + else + current_param += char + end + end + + # Add the last parameter + param_parts << current_param.strip if !current_param.strip.empty? + + # If only one parameter or very short, don't break + if param_parts.length <= 1 + return proto + end + + # Build the formatted result + align_pos = before_params.length + align = " " * align_pos + result = before_params + param_parts.shift + + param_parts.each do |part| + result += ",\n" + indent + align + part + end + + result += after_params + return result end # Given the source code array and the index at which an exported symbol was @@ -72,14 +123,43 @@ def docufy(src,i) m = /VM_[A-z0-9]+/.match(src[i]) shortname = m[0].sub("VM_","") name = "ValkeyModule_" ++ shortname - proto = src[i].sub("{","").strip+";\n" + + # Build the complete function prototype by reading until we find the opening brace + proto_lines = [] + j = i + while j < src.length + line = src[j].rstrip + if line.include?("{") + # Include the part before the brace + line = line.sub(/\s*\{.*$/, "") + proto_lines << line unless line.strip.empty? + break + else + proto_lines << line + end + j += 1 + end + + # Join all lines and clean up + proto = proto_lines.join(" ") + + # Remove extra whitespace and normalize + proto = proto.gsub(/\s+/, " ").strip + + # Replace VM_ with ValkeyModule_ proto = proto.sub("VM_","ValkeyModule_") + + # Add semicolon if not present + proto += ";" unless proto.end_with?(";") + + # Apply line breaking proto = linebreak_proto(proto, " "); + # Add a link target with the function name. (We don't trust the exact id of # the generated one, which depends on the Markdown implementation.) puts "\n\n" puts "### `#{name}`\n\n" - puts " #{proto}\n" + puts " #{proto}\n\n" puts "**Available since:** #{$since[shortname] or "unreleased"}\n\n" comment = "" while true