diff --git a/apis/FrameRate/IFrameRate.h b/apis/FrameRate/IFrameRate.h
index b848ad52..d483569e 100644
--- a/apis/FrameRate/IFrameRate.h
+++ b/apis/FrameRate/IFrameRate.h
@@ -27,6 +27,7 @@ namespace WPEFramework
namespace Exchange
{
/* @json 1.0.0 @text:keep */
+ // @plugindescription This plugin allows to collect FPS related data on TV profile stack.
struct EXTERNAL IFrameRate : virtual public Core::IUnknown
{
enum { ID = ID_FRAMERATE };
@@ -37,79 +38,94 @@ namespace WPEFramework
enum { ID = ID_FRAMERATE_NOTIFICATION };
// @text onFpsEvent
- // @brief Triggered by callback from FrameRate after onFpsEvent
- // @param average - in - int
- // @param min - in - int
- // @param max - in - int
- virtual void OnFpsEvent(const int average, const int min, const int max) {};
+ // @details Triggered at the end of each interval as defined by the `setCollectionFrequency` method
+ // after `startFpsCollection` method and once after the `stopFpsCollection` method is invoked
+ // @param average: The average FPS e.g. 60
+ // @param min: The minimum FPS e.g. 30
+ // @param max: The maximum FPS e.g. 120
+ virtual void OnFpsEvent(const int average /* @in */, const int min /* @in */, const int max /* @in */) {};
// @text onDisplayFrameRateChanging
- // @brief Triggered when the framerate changes started
- // @param displayFrameRate - in - string
- virtual void OnDisplayFrameRateChanging(const string& displayFrameRate) {};
+ // @details This event is triggered when the display frame rate is about to change
+ // and represented as "WIDTHxHEIGHTxFPS".
+ // @param displayFrameRate: The new display frame rate e.g. "1920x1080x60"
+ virtual void OnDisplayFrameRateChanging(const string& displayFrameRate /* @in */) {};
// @text onDisplayFrameRateChanged
- // @brief Triggered when the framerate changed.
- // @param displayFrameRate - in - string
- virtual void OnDisplayFrameRateChanged(const string& displayFrameRate) {};
+ // @details This event is triggered after the display frame rate has changed
+ // and represented as "WIDTHxHEIGHTxFPS".
+ // @param displayFrameRate: The new display frame rate e.g. "1920x1080x60"
+ virtual void OnDisplayFrameRateChanged(const string& displayFrameRate /* @in */) {};
};
+ // @omit
virtual Core::hresult Register(IFrameRate::INotification* notification) = 0;
+ // @omit
virtual Core::hresult Unregister(IFrameRate::INotification* notification) = 0;
/** Gets the Display Frame Rate*/
// @text getDisplayFrameRate
- // @brief Gets the current display frame rate values.
- // @param framerate - out - string
- // @param success - out - boolean
+ // @details Retrieves the current display frame rate as a string in the format "WIDTHxHEIGHTpxFPS"
+ // @param framerate: The current display frame rate. e.g. "3840x2160px60"
+ // @param success: Indicates if the operation was successful e.g. True
+ // @returns Core::hresult
virtual Core::hresult GetDisplayFrameRate(string& framerate /* @out */, bool& success /* @out */) = 0;
/** Gets framerate mode */
// @text getFrmMode
- // @brief Gets the current auto framerate mode.
- // @param frmmode - out - int
- // @param success - out - boolean
+ // @details Retrieves the current auto framerate mode as an integer. Expeted values are 0 or 1.
+ // @param auto-frm-mode: The current auto framerate mode. e.g. 1
+ // @param success: Indicates if the operation was successful. e.g. True
+ // @returns Core::hresult
virtual Core::hresult GetFrmMode(int &framerateMode /* @out @text:auto-frm-mode */, bool& success /* @out */) = 0;
/** Sets the FPS data collection interval */
// @text setCollectionFrequency
- // @brief Sets the FPS data collection interval.
- // @param frequency - in - int
- // @param success - out - boolean
- virtual Core::hresult SetCollectionFrequency(const int frequency, bool& success /* @out */) = 0;
+ // @details Sets the interval for FPS data collection in milliseconds. Default is 10000ms and min is 100ms
+ // @param frequency: The collection frequency in ms. e.g. 1000
+ // @param success: Indicates if the operation was successful. e.g. True
+ // @returns Core::hresult
+ virtual Core::hresult SetCollectionFrequency(const int frequency /* @in */, bool& success /* @out */) = 0;
/** Sets the display framerate values */
// @text setDisplayFrameRate
- // @brief Sets the display framerate values.
- // @param framerate - in - string
- // @param success - out - boolean
- virtual Core::hresult SetDisplayFrameRate(const string& framerate, bool& success /* @out */) = 0;
+ // @details Sets the display framerate to the specified value in the format "WIDTHxHEIGHTpxFPS".
+ // @param framerate: The display framerate to set. e.g. "3840px2160px30"
+ // @param success: Indicates if the operation was successful. e.g. True
+ // @returns Core::hresult
+ virtual Core::hresult SetDisplayFrameRate(const string& framerate /* @in */, bool& success /* @out */) = 0;
/** Sets the auto framerate mode */
// @text setFrmMode
- // @brief Set the Frm mode.
- // @param frmmode - in - int
- // @param success - out - boolean
+ // @details Sets the auto framerate mode to the specified value. Expected values are 0(disable) or 1(enable).
+ // @param frmmode: The framerate mode to set. e.g. 1
+ // @param success: Indicates if the operation was successful. e.g. True
+ // @returns Core::hresult
virtual Core::hresult SetFrmMode(const int frmmode /* @in */, bool& success /* @out */) = 0;
/** Starts the FPS data collection */
// @text startFpsCollection
- // @brief Starts the FPS data collection. Starts the FPS data collection
- // @param success - out - boolean
+ // @details Starts collecting FPS data at the configured interval set by the method `SetCollectionFrequency`.
+ // @see onFpsEvent
+ // @param success: Indicates if the operation was successful. e.g. True
+ // @returns Core::hresult
virtual Core::hresult StartFpsCollection(bool& success /* @out */) = 0;
/** Stops the FPS data collection */
// @text stopFpsCollection
- // @brief Stops the FPS data collection
- // @param success - out - boolean
+ // @details Stops the FPS data collection.
+ // @param success: Indicates if the operation was successful. e.g. True
+ // @returns Core::hresult
virtual Core::hresult StopFpsCollection(bool& success /* @out */) = 0;
/** Update the FPS value */
// @text updateFps
- // @brief Update the FPS value
- // @param newFpsValue - in - int
- // @param success - out - boolean
- virtual Core::hresult UpdateFps(const int newFpsValue, bool& success /* @out */) = 0;
+ // @details Updates the current FPS value to the specified value represented as integer.
+ // @see onFpsEvent
+ // @param newFpsValue: The new FPS value. e.g. 60
+ // @param success: Indicates if the operation was successful. e.g. True
+ // @returns Core::hresult
+ virtual Core::hresult UpdateFps(const int newFpsValue /* @in */, bool& success /* @out */) = 0;
};
} // namespace Exchange
} // namespace WPEFramework
diff --git a/tools/md_from_h_generator/README.md b/tools/md_from_h_generator/README.md
index d899afb5..aaa16dec 100644
--- a/tools/md_from_h_generator/README.md
+++ b/tools/md_from_h_generator/README.md
@@ -42,7 +42,7 @@ virtual uint32_t initialize();
- **Required**: Yes (Mandatory tag for all methods/properties/events)
- **Usage**:
- Use this tag for a short, one-line description of the method.
- - The description following this tag will be shown on the method/property/event table of contents
+ - The description following this tag will be shown on the method/property/event table of contents
### Example:
@@ -98,7 +98,7 @@ virtual uint32_t initialize();
- **Required**: No (Optional tag)
- **Usage**:
- Use this tag to reference related methods, classes, or external documentation.
- - The linked event name should appear exactly as it is declared, without parenthesis
+ - The linked event name should appear exactly as it is declared, without parenthesis
- This tag is optional, but should be used if a corresponding event is defined in INotifications
### Example:
@@ -140,28 +140,29 @@ virtual void onInitialize();
- Parameter/symbol examples should be defined here (see [Providing Symbol Examples](#providing_examples))
- Specify the parameter name and its description. Format can include colon i.e. `@param [param_name]: [description]` or `@param [para_name] [description]`
- IMPORTANTLY, in addition to using the param tag, mark each parameter with inline 'in/out' information in the parameter list. If a parameter does not have inline in/out information, it defaults to 'in'.
+ - Additionally a parameter name override can be specified by combining `@in` or `@out` followed by `@text:varible-override-name` in the function declaration.
### Example:
***Header File Example:***
```cpp
-/**
+/**
...
* @param configPath: The config file path for initialization e.g. "../build/test.conf"
* @param status The status of the initialization. Set to true if completed.
*/
-virtual uint32_t initialize(const string& configPath /* @in */, bool status /* @out */);
+virtual uint32_t initialize(const string& configPath /* @in @text:config-path */, bool status /* @out @text:status-response */);
```
***Generated Markdown Example:***
> ### Parameters
> | Name | Type | Description |
> | :-------- | :-------- | :-------- |
-> | config | string | The config file path for initialization |
+> | config-path | string | The config file path for initialization |
> ### Results
> | Name | Type | Description |
> | :-------- | :-------- | :-------- |
-> | status | bool | The status of the initialization. Set to true if completed. |
+> | status-response | bool | The status of the initialization. Set to true if completed. |
---
@@ -209,7 +210,7 @@ virtual uint32_t internalMethod();
**Example**:
```cpp
-/*
+/*
* @property
* @brief Video output port on the STB used for connection to TV
* @param name: video output port name
@@ -217,13 +218,40 @@ virtual uint32_t internalMethod();
virtual uint32_t PortName (string& name /* @out */) const = 0;
```
+### 9. `@plugindescription`
+- **Purpose**: Provides option to override the generic plugin description text
+- **Required**: No (Mandatory tag if method is a property)
+- **Usage**:
+ - Use this tag for overriding the generic plugin description.
+
+**Example**:
+```cpp
+ namespace Exchange
+ {
+ /* @json 1.0.0 @text:keep */
+ // @plugindescription This plugin provides so and so functionalities
+ struct EXTERNAL IClassName : virtual public Core::IUnknown
+ {
+```
+
+***Generated Markdown Example:***
+
+>
+> # Description
+>
+> This plugin provides so and so functionalities
+>
+> The plugin is designed to be loaded and executed within the Thunder framework. For more information about the framework refer > > to [[Thunder](#ref.Thunder)].
+>
+>
+
---
## 4. Additional Features and Guidelines
-### Providing Symbol Examples
+### Providing Symbol Examples
In the RDK Services and Entservices APIs, plugins communicate using RPC. To facilitate this, the documentation includes relevant examples of request and response JSON structures. The md_from_h tool automates the creation of these examples by parsing enums, structs, iterators, and basic types declared in the header file, as well as extracting examples from @param Doxygen tags.
The tool maintains a global symbol registry to track the names and types of parameters declared in methods, properties, events, enums, and struct members. The goal of the global symbol registry is to make it easier and more consistent to provide examples for symbols which appear multiple times in a header file (such as preferredLanguages in IUserSettings.h). Examples are generated by analyzing the @param tags, where the tool uses a regular expression to extract text following the pattern `e.g. "(.*)"` in the parameter description. The value inside the quotes is then used as the example for that symbol. The pattern `ex: (.*)` is also matched in cases where examples have double-quotes. Additionally, examples can be derived from structs if their members include descriptive comments.
@@ -258,7 +286,7 @@ The following demonstrates how examples are set for struct members:
***Header File Example:***
```cpp
-struct USBDevice {
+struct USBDevice {
uint8_t deviceClass /* @brief USB class of the device as per USB specification e.g. "10" */ ;
uint8_t deviceSubclass /* @brief USB sub class of the device as per USB specification e.g. "6" */;
string deviceName /* @brief Name of the USB device e.g. "001/003"*/;
diff --git a/tools/md_from_h_generator/generate_md_from_header.py b/tools/md_from_h_generator/generate_md_from_header.py
index 14f706e4..763d1b48 100644
--- a/tools/md_from_h_generator/generate_md_from_header.py
+++ b/tools/md_from_h_generator/generate_md_from_header.py
@@ -35,34 +35,35 @@ def generate_md_from_header(header_file):
filename = os.path.basename(header_file)
classname, _ = os.path.splitext(filename)
- # Remove the leading 'I' from the api's class name
- output_file_path = './tools/md_from_h_generator/generated_docs/' + classname[1:] + 'Plugin.md'
-
log_file_path = './tools/md_from_h_generator/logs/' + classname + '.txt'
logger = Logger(log_file_path)
header_structure = HeaderFileParser(header_file, logger)
+ header_structure.build_all_canonical_dicts()
+
+ # Remove the leading 'I' from the api's class name
+ output_file_path = './tools/md_from_h_generator/generated_docs/' + header_structure.classname + 'Plugin.md'
with open(output_file_path, 'w', encoding='utf-8') as file:
- file.write(generate_header_toc(classname, header_structure))
- file.write(generate_header_description_markdown(classname))
+ file.write(generate_header_toc(header_structure.classname, header_structure))
+ file.write(generate_header_description_markdown(header_structure.classname, getattr(header_structure, 'plugindescription', '')))
if len(header_structure.methods.values()) > 0:
- file.write(generate_methods_toc(header_structure.methods, classname))
- for method_name, method_info in header_structure.methods.items():
+ file.write(generate_methods_toc(header_structure.methods, header_structure.classname))
+ for idx, (method_name, method_info) in enumerate(header_structure.methods.items(), start=1):
file.write(generate_method_markdown(
- method_name, method_info, header_structure.symbols_registry))
+ method_name, method_info, header_structure.symbols_registry, header_structure.classname, idx, header_structure.events))
file.write("\n")
if len(header_structure.properties.values()) > 0:
- file.write(generate_properties_toc(header_structure.properties, classname))
- for prop_name, prop_info in header_structure.properties.items():
+ file.write(generate_properties_toc(header_structure.properties, header_structure.classname))
+ for idx, (prop_name, prop_info) in enumerate(header_structure.properties.items(), start=1):
file.write(generate_property_markdown(
- prop_name,prop_info, header_structure.symbols_registry))
+ prop_name, prop_info, header_structure.symbols_registry, header_structure.classname))
file.write("\n")
if len(header_structure.events.values()) > 0:
- file.write(generate_notifications_toc(header_structure.events, classname))
- for event_name, event_info in header_structure.events.items():
+ file.write(generate_notifications_toc(header_structure.events, header_structure.classname))
+ for idx, (event_name, event_info) in enumerate(header_structure.events.items(), start=1):
file.write(generate_notification_markdown(
- event_name, event_info, header_structure.symbols_registry))
+ event_name, event_info, header_structure.symbols_registry, header_structure.classname, idx))
logger.write_log()
logger.close()
diff --git a/tools/md_from_h_generator/header_file_parser.py b/tools/md_from_h_generator/header_file_parser.py
index 2b1d7c7a..6731ac3a 100644
--- a/tools/md_from_h_generator/header_file_parser.py
+++ b/tools/md_from_h_generator/header_file_parser.py
@@ -31,14 +31,16 @@ class HeaderFileParser:
"""
# List of regexes to match different components of the header file
REGEX_LINE_LIST = [
+ ('plugindescription', 'doxygen', re.compile(r'(?:\/\*+|\*|//)\s*@plugindescription\s+(.*?)(?=\s*\*\/|$)')),
+ ] + [
('text', 'doxygen', re.compile(r'(?:\/\*+|\*|//) (?:@text|@alt)\s+(.*?)(?=\s*\*\/|$)')),
('brief', 'doxygen', re.compile(r'(?:\/\*+|\*|//) @brief\s*(.*?)(?=\s*\*\/|$)')),
('details', 'doxygen', re.compile(r'(?:\/\*+|\*|//) @details\s*(.*?)(?=\s*\*\/|$)')),
- ('params', 'doxygen', re.compile(r'(?:\/\*+|\*|//) @param(?:\[.*\])?\s+(\w+)\s*\:?\s*(.*?)(?=\s*\*\/|$)')),
+ ('params', 'doxygen', re.compile(r'(?:\/\*+|\*|//)\s*@param(?:\[.*\])?\s+([^\s:]+)\s*:?\s*(.*?)(?=\s*\*\/|$)')),
('return', 'doxygen', re.compile(r'(?:\/\*+|\*|//) @return(?:s)?\s*(.*?)(?=\s*\*\/|$)')),
('see', 'doxygen', re.compile(r'(?:\/\*+|\*|//) @see\s*(.*?)(?=\s*\*\/|$)')),
('omit', 'doxygen', re.compile(r'(?:\/\*+|\*|//)\s*(@json:omit|@omit)')),
- ('property','doxygen', re.compile(r'(?:\/\*+|\*|//) @property\s*(.*)')),
+ ('property','doxygen', re.compile(r'(?:\/\*+|\*|//) @property\s*(.*)')),
('comment', 'doxygen', re.compile(r'(?:\/\*+|\*|//)\s*(.*)')),
('enum', 'cpp_obj', re.compile(r'enum\s+([\w\d]+)\s*(?:\:\s*([\w\d\:\*]*))?\s*\{?')),
('struct', 'cpp_obj', re.compile(r'struct\s+(EXTERNAL\s+)?([\w\d]+)\s*(?:\{)?(?!.*:)')),
@@ -47,15 +49,15 @@ class HeaderFileParser:
]
# Basic type examples for generating missing symbol examples
BASIC_TYPE_EXAMPLES = {
- 'int32_t': '0',
- 'uint32_t': '0',
- 'int64_t': '0',
+ 'int32_t': '0',
+ 'uint32_t': '0',
+ 'int64_t': '0',
'uint64_t': '0',
'int': '0',
- 'float': '0.0',
- 'double': '0.0',
- 'bool': 'true',
- 'char': 'a',
+ 'float': '0.0',
+ 'double': '0.0',
+ 'bool': True,
+ 'char': 'a',
'string': ''
}
# List of regexes to match different cpp components of the header file
@@ -72,16 +74,17 @@ class HeaderFileParser:
def __init__(self, header_file_path: str, logger: Logger):
"""
- Initializes data structures to track different components of a C++ header file, then
+ Initializes data structures to track different components of a C++ header file, then
parses said header file to extract methods, structs, enums, and iterators.
- Args:
+ Args:
header_file_path (str): path to the header file
logger (Logger): list of regex matching different components of the header file
"""
# objects to hold the different components and properties of the header file
self.header_file_path = header_file_path
- self.classname = os.path.splitext(os.path.basename(self.header_file_path))[0]
+ # All the header files will begin with "I", strip it to get the classname.
+ self.classname = os.path.splitext(os.path.basename(self.header_file_path))[0][1:]
self.methods = {}
self.properties = {}
self.events = {}
@@ -127,7 +130,7 @@ def post_process(self):
def parse_header_file(self):
"""
- Parses the header file line-by-line to track and record the file's components, such as
+ Parses the header file line-by-line to track and record the file's components, such as
methods, properties, events, structs, enums, and iterators. Keeps track of these components'
associated doxygen tags.
"""
@@ -186,7 +189,7 @@ def match_line_with_regex(self, line, line_regex_list):
return match.groups(), tag, l_type
return None, None, None
- def process_method(self, line, method_object, within_method_def, method_paren_count,
+ def process_method(self, line, method_object, within_method_def, method_paren_count,
curr_line_num, scope):
"""
Processes a line within a method definition.
@@ -226,7 +229,7 @@ def process_enum(self, line, enum_object, within_enum_def, enum_braces_count, cu
enum_object = ''
return enum_object, enum_braces_count, within_enum_def
- def process_struct(self, line, struct_object, within_struct_def, struct_braces_count,
+ def process_struct(self, line, struct_object, within_struct_def, struct_braces_count,
curr_line_num):
"""
Processes a line within a struct definition.
@@ -248,32 +251,39 @@ def process_struct(self, line, struct_object, within_struct_def, struct_braces_c
def update_doxy_tags(self, groups, line_tag):
"""
- Updates the doxygen tag object with the given line's information.
+ Updates the doxygen tag object with the given line's information.
+ Supports multiline for all tags by accumulating lines until a new tag is found.
"""
- if line_tag == 'text':
- # self.doxy_tags = {}
+ if line_tag == 'plugindescription':
+ self.plugindescription = groups[0]
+ elif line_tag == 'text':
self.doxy_tags['text'] = groups[0]
+ self.latest_tag = 'text'
elif line_tag == 'params':
self.latest_param = groups[0]
self.latest_tag = 'params'
self.doxy_tags.setdefault('params', {})[self.latest_param] = groups[1]
elif line_tag == 'see':
self.doxy_tags.setdefault('see', {})[groups[0]] = ''
+ self.latest_tag = 'see'
elif line_tag == 'comment':
if groups[0] == '/':
return
- elif self.latest_tag == 'params':
+ # Multiline support: append to last tag
+ if self.latest_tag == 'params':
self.doxy_tags['params'][self.latest_param] += (' ' + groups[0])
- elif self.latest_tag:
+ elif self.latest_tag and self.latest_tag in self.doxy_tags and self.latest_tag != 'plugindescription':
self.doxy_tags[self.latest_tag] += (' ' + groups[0])
line_tag = self.latest_tag
else:
self.doxy_tags[line_tag] = groups[0]
- self.latest_tag = line_tag
+ self.latest_tag = line_tag
+ if line_tag != 'plugindescription':
+ self.latest_tag = line_tag
def clean_and_validate_cpp_obj_line(self, line, delimiter, line_num, data_type):
"""
- Validates a line of a multi-line cpp object by checking that data members are defined on
+ Validates a line of a multi-line cpp object by checking that data members are defined on
separate lines and that comments are formed before the delimiter.
"""
delim_index = line.find(delimiter)
@@ -288,7 +298,7 @@ def clean_and_validate_cpp_obj_line(self, line, delimiter, line_num, data_type):
self.logger.log("WARNING",
f"Line {line_num + 1} should have only one {data_type} per line.")
return line
-
+
def remove_inline_comments(self, line):
"""
Removes inline comments from a line.
@@ -334,7 +344,7 @@ def register_enum(self, enum_object):
description = self.clean_description(description)
enumerator_value = enumerator_value or len(self.enums_registry[enum_name])
self.enums_registry[enum_name][enumerator_name] = {
- 'value': enumerator_value,
+ 'value': enumerator_value,
'description': description.strip() if description else ''
}
else:
@@ -356,7 +366,7 @@ def register_struct(self, struct_object):
member_type, member_name, description = member_match.groups()
description = self.clean_description(description)
self.structs_registry[struct_name][member_name] = {
- 'type': member_type,
+ 'type': member_type,
'description': description.strip() if description else ''
}
# register each data member in the global symbol registry
@@ -392,14 +402,14 @@ def register_method(self, method_object, doxy_tags, scope):
def build_method_info(self, method_return_type, method_parameters, doxy_tags):
"""
- Helper to build a method info object. Also registers method parameters in the symbol
+ Helper to build a method info object. Also registers method parameters in the symbol
registry.
"""
doxy_tag_param_info = doxy_tags.get('params', {})
params, results = self.process_and_register_params(method_parameters, doxy_tag_param_info)
method_info = {
- 'text': doxy_tags.get('text', ''),
- 'brief': doxy_tags.get('brief', ''),
+ 'text': doxy_tags.get('text', ''),
+ 'brief': doxy_tags.get('brief', ''),
'details': doxy_tags.get('details', ''),
'events': doxy_tags.get('see', {}),
'params': params,
@@ -415,31 +425,68 @@ def build_method_info(self, method_return_type, method_parameters, doxy_tags):
def process_and_register_params(self, method_parameters, doxy_tag_param_info):
"""
- Helper to build params and results data structures, using the parameter declaration list
+ Helper to build params and results data structures, using the parameter declaration list
and doxygen tags.
"""
+ def normalize_key(key):
+ return key.replace('_', '-').lower().strip()
+
+ # Build a normalized lookup for param descriptions
+ normalized_param_info = {normalize_key(k): v for k, v in doxy_tag_param_info.items()}
+
+ # DEBUG: Print the doxygen param info and normalized lookup
+ self.logger.log("INFO", f"doxy_tag_param_info: {doxy_tag_param_info}")
+ self.logger.log("INFO", f"normalized_param_info: {normalized_param_info}")
+
param_list_info = self.get_info_from_param_declaration(method_parameters)
+ self.logger.log("INFO", f"param_list_info: {param_list_info}")
params = []
results = []
- # build the params and results lists using the parameter delcaration list and doxygen tags
- for symbol_name, (symbol_type, symbol_inline_comment) in param_list_info.items():
- # register string iterators here b/c they are seldom defined outside of a method param
+ for symbol_name, (symbol_type, symbol_inline_comment, custom_name, direction) in param_list_info.items():
+ self.logger.log("INFO", f"Processing param: symbol_name={symbol_name}, symbol_type={symbol_type}, custom_name={custom_name}, direction={direction}, symbol_inline_comment={symbol_inline_comment}")
if symbol_type == 'RPC::IStringIterator':
self.register_iterator(symbol_type)
symbol_description = doxy_tag_param_info.get(symbol_name, '')
+ custom_description = None
+ if symbol_inline_comment:
+ text_match = re.search(r'@text:([\w\-]+)', symbol_inline_comment)
+ if text_match:
+ override_name = text_match.group(1)
+ custom_name = override_name
+ norm_override = normalize_key(override_name)
+ self.logger.log("INFO", f"Found @text override: override_name={override_name}, norm_override={norm_override}")
+ # Prefer description from normalized doxygen @param for override name
+ if norm_override in normalized_param_info:
+ custom_description = normalized_param_info[norm_override]
+ self.logger.log("INFO", f"Found custom_description for override: {custom_description}")
+ else:
+ self.logger.log("INFO", f"Override param name '{override_name}' not found in @param tags for method param '{symbol_name}'.")
+ self.logger.log("WARNING", f"Override param name '{override_name}' not found in @param tags for method param '{symbol_name}'.")
+ # Fallback to original param name if not found
+ if not custom_description and normalize_key(symbol_name) in normalized_param_info:
+ custom_description = normalized_param_info[normalize_key(symbol_name)]
+ self.logger.log("INFO", f"Fallback to original param name: {custom_description}")
+ if not custom_description and normalize_key(symbol_name) in normalized_param_info:
+ custom_description = normalized_param_info[normalize_key(symbol_name)]
+ self.logger.log("INFO", f"No override, using original param name: {custom_description}")
+ if custom_description:
+ custom_description = re.sub(r'e\.g\.\s*\".*?(? 2:
+ parts[2] = classname
+ request['method'] = '.'.join(parts)
+ # Set the id
+ if isinstance(request, dict):
+ request = dict(request) # shallow copy
+ request['id'] = example_id
+ request_json = json.dumps(_convert_json_types(request), indent=4)
+ markdown = EXAMPLE_REQUEST_TEMPLATE.format(request_json=request_json, method_type=method_type)
return markdown
-def generate_events_section(events):
+def generate_response_section(response, method_type, classname=None, example_id=42):
"""
- Generate the events section for a method.
+ Generate the response section for a method.
"""
- markdown = "### Events\n"
- if events:
- markdown += """| Event | Description |\n| :-------- | :-------- |\n"""
- for event in events:
- markdown += f"| [{event}](#event.{event}) | {events[event]} |\n"
- else:
- markdown += "No events are associated with this method.\n"
+ if isinstance(response, dict):
+ response = dict(response)
+ response['id'] = example_id
+ response_json = json.dumps(_convert_json_types(response), indent=4)
+ markdown = EXAMPLE_RESPONSE_TEMPLATE.format(response_json=response_json, method_type=method_type)
return markdown
def generate_parameters_section(params, symbol_registry):
"""
- Generate the parameters section for a method.
+ Generate the parameters section for a method, showing the parent object and all fields for all params, using override names and descriptions if present.
"""
markdown = "### Parameters\n"
if params:
- markdown += """| Name | Type | Description |\n| :-------- | :-------- | :-------- |\n"""
+ markdown += "| Name | Type | Description |\n| :-------- | :-------- | :-------- |\n"
+ markdown += f"| params | object | |\n"
for param in params:
- flattened_params = symbol_registry[f"{param['name']}-{param['type']}"]['flattened_description']
- for param_name, param_data in flattened_params.items():
- cleaned_description = re.sub(r'e\.g\.\s*\".*?(? 2:
+ parts[2] = classname
+ request['method'] = '.'.join(parts)
+ if isinstance(request, dict):
+ request = dict(request)
+ request['id'] = example_id
+ request_json = json.dumps(_convert_json_types(request), indent=4)
+ markdown += EXAMPLE_NOTIFICATION_TEMPLATE.format(request_json=request_json)
return markdown
+
+def _convert_json_types(obj):
+ """
+ Recursively convert string numbers and 'true'/'false' strings to int/float/bool in a dict or list.
+ """
+ if isinstance(obj, dict):
+ return {k: _convert_json_types(v) for k, v in obj.items()}
+ elif isinstance(obj, list):
+ return [_convert_json_types(i) for i in obj]
+ elif isinstance(obj, str):
+ if obj.lower() == 'true':
+ return True
+ if obj.lower() == 'false':
+ return False
+ try:
+ if '.' in obj:
+ return float(obj)
+ return int(obj)
+ except ValueError:
+ return obj
+ else:
+ return obj