Update module api generation and format module.c (#2757)

This change makes it so that the module API reference can be cleanly
generated from the module.c file. Most of this seems to be because of
the code formatting work we did. There are two parts:
1. Updated some of the odd corner cases in module.c so they can be
handled. For example, there was a method that had none of the method on
the first line, which was unhandled. None of these are functional and I
think format should be OK with them.
2. Better handle multi-line function prototypes in the ruby code. Before
we just relied on the function to be on a single line, now we handle it
on multiple lines. This feels pretty hacked in, but I don't really
understand the rest of the code but it does work.

Generated this PR:
https://github.com/valkey-io/valkey-doc/pull/374/files.

---------

Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
This commit is contained in:
Madelyn Olson 2025-10-21 20:28:40 -07:00 committed by GitHub
parent 9acea36841
commit 35b4e2f1ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 105 additions and 24 deletions

View File

@ -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;

View File

@ -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 "<span id=\"#{name}\"></span>\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