mirror of
https://github.com/Mr-X-GTA/YimMenu.git
synced 2024-12-22 12:07:46 +08:00
725 lines
22 KiB
Python
725 lines
22 KiB
Python
import os
|
|
from enum import Enum
|
|
|
|
src_folder = "../src/"
|
|
|
|
lua_api_comment_identifier = "lua api"
|
|
lua_api_comment_separator = ":"
|
|
|
|
tables = {}
|
|
classes = {}
|
|
functions = {}
|
|
|
|
|
|
class DocKind(Enum):
|
|
Table = "table"
|
|
Class = "class"
|
|
Field = "field"
|
|
Constructor = "constructor"
|
|
Function = "function"
|
|
Tabs = "tabs"
|
|
Infraction = "infraction"
|
|
|
|
|
|
class Table:
|
|
def __init__(self, name, fields, functions, description):
|
|
self.name = name.strip()
|
|
self.fields = fields
|
|
self.functions = functions
|
|
self.description = description
|
|
|
|
def __str__(self):
|
|
s = f"# Table: {self.name}\n"
|
|
s += "\n"
|
|
|
|
if len(self.description) > 0:
|
|
s += f"{self.description}\n"
|
|
s += "\n"
|
|
|
|
if len(self.fields) > 0:
|
|
s += f"## Fields ({len(self.fields)})\n"
|
|
s += "\n"
|
|
|
|
self.check_for_duplicate_fields_names()
|
|
|
|
for field in self.fields:
|
|
s += field.print_markdown()
|
|
|
|
if len(self.functions) > 0:
|
|
s += f"## Functions ({len(self.functions)})\n"
|
|
s += "\n"
|
|
|
|
for func in self.functions:
|
|
s += func.print_markdown(f"{self.name}.")
|
|
|
|
s += "\n"
|
|
|
|
return s
|
|
|
|
def check_for_duplicate_fields_names(self):
|
|
seen = set()
|
|
duplicates = [x for x in self.fields if x.name in seen or seen.add(x.name)]
|
|
if len(duplicates) > 0:
|
|
print("Error while building lua doc. Duplicate field names:")
|
|
for dup in duplicates:
|
|
print(dup)
|
|
exit(1)
|
|
|
|
|
|
class Class:
|
|
def __init__(self, name, inheritance, fields, constructors, functions, description):
|
|
self.name = name.strip()
|
|
self.inheritance = inheritance
|
|
self.fields = fields
|
|
self.constructors = constructors
|
|
self.functions = functions
|
|
self.description = description
|
|
|
|
def __str__(self):
|
|
s = f"# Class: {self.name}\n"
|
|
s += "\n"
|
|
|
|
if len(self.inheritance) > 0:
|
|
inherited_class_names = ", ".join(self.inheritance)
|
|
s += f"## Inherit from {len(self.inheritance)} class: {inherited_class_names}\n"
|
|
s += "\n"
|
|
|
|
if len(self.description) > 0:
|
|
s += f"{self.description}\n"
|
|
s += "\n"
|
|
|
|
if len(self.fields) > 0:
|
|
s += f"## Fields ({len(self.fields)})\n"
|
|
s += "\n"
|
|
|
|
self.check_for_duplicate_fields_names()
|
|
|
|
for field in self.fields:
|
|
s += field.print_markdown()
|
|
|
|
if len(self.constructors) > 0:
|
|
s += f"## Constructors ({len(self.constructors)})\n"
|
|
s += "\n"
|
|
|
|
for ctor in self.constructors:
|
|
s += ctor.print_markdown()
|
|
|
|
if len(self.functions) > 0:
|
|
s += f"## Functions ({len(self.functions)})\n"
|
|
s += "\n"
|
|
|
|
for func in self.functions:
|
|
s += func.print_markdown(f"{self.name}:")
|
|
|
|
s += "\n"
|
|
|
|
return s
|
|
|
|
def check_for_duplicate_fields_names(self):
|
|
seen = set()
|
|
duplicates = [x for x in self.fields if x.name in seen or seen.add(x.name)]
|
|
if len(duplicates) > 0:
|
|
print("Error while building lua doc. Duplicate field names:")
|
|
for dup in duplicates:
|
|
print(dup)
|
|
exit(1)
|
|
|
|
|
|
class Field:
|
|
def __init__(self, name, type_, description):
|
|
self.name = name.strip()
|
|
self.type_ = type_.strip()
|
|
self.description = description
|
|
|
|
def __str__(self):
|
|
s = f"Field: {self.name}\n"
|
|
s += f"Type: {self.type_}\n"
|
|
s += f"Description: {self.description.strip()}\n"
|
|
return s
|
|
|
|
def print_markdown(self):
|
|
s = ""
|
|
|
|
s += f"### `{self.name}`\n"
|
|
s += "\n"
|
|
|
|
if len(self.description) > 0:
|
|
s += f"{self.description}\n"
|
|
s += "\n"
|
|
|
|
if self.type_ is not None and len(self.type_) > 0:
|
|
s += f"- Type: `{self.type_}`\n"
|
|
|
|
s += "\n"
|
|
|
|
return s
|
|
|
|
|
|
class Constructor:
|
|
def __init__(self, parent, parameters, description):
|
|
self.parent = parent
|
|
self.parameters = parameters
|
|
self.description = description
|
|
|
|
def print_markdown(self):
|
|
s = ""
|
|
|
|
parameters_str = ", ".join(p.name for p in self.parameters)
|
|
|
|
s += f"### `new({parameters_str})`\n"
|
|
s += "\n"
|
|
|
|
if len(self.description) > 0:
|
|
s += f"{self.description}\n"
|
|
s += "\n"
|
|
|
|
if len(self.parameters) > 0:
|
|
s += f"- **Parameters:**\n"
|
|
for param in self.parameters:
|
|
s += f" - `{param.name}` ({param.type_})"
|
|
if len(param.description) > 0:
|
|
s += f": {param.description}\n"
|
|
else:
|
|
s += f"\n"
|
|
s += "\n"
|
|
|
|
s += f"**Example Usage:**\n"
|
|
s += "```lua\n"
|
|
|
|
s += f"myInstance = {self.parent.name}:new({parameters_str})\n"
|
|
|
|
s += "```\n"
|
|
s += "\n"
|
|
|
|
return s
|
|
|
|
|
|
class Parameter:
|
|
def __init__(self, name, type_, description):
|
|
self.name = name.strip()
|
|
self.type_ = type_.strip()
|
|
self.description = description
|
|
|
|
def __str__(self):
|
|
s = f"Parameter: {self.name}\n"
|
|
s += f"Type: {self.type_}\n"
|
|
s += f"Description: {self.description.strip()}\n"
|
|
return s
|
|
|
|
|
|
class Function:
|
|
def __init__(
|
|
self, name, parent, parameters, return_type, return_description, description
|
|
):
|
|
self.name = name.strip()
|
|
self.parent = parent
|
|
self.parameters = parameters
|
|
self.return_type = return_type
|
|
self.return_description = return_description
|
|
self.description = description
|
|
|
|
def __str__(self):
|
|
s = f"Function: {self.name}\n"
|
|
|
|
type_name = str(type(self.parent)).split(".")[1][:-2]
|
|
s += f"Parent: {self.parent.name} ({type_name})\n"
|
|
|
|
s += f"Parameters: {len(self.parameters)}\n"
|
|
i = 1
|
|
for param in self.parameters:
|
|
s += f"Parameter {i}\n"
|
|
s += str(param) + "\n"
|
|
i += 1
|
|
|
|
s += f"Return Type: {self.return_type}\n"
|
|
s += f"Return Description: {self.return_description}\n"
|
|
|
|
s += f"Description: {self.description}\n"
|
|
return s
|
|
|
|
def print_markdown(self, prefix):
|
|
s = ""
|
|
|
|
parameters_str = ", ".join(p.name for p in self.parameters)
|
|
|
|
s += f"### `{self.name}({parameters_str})`\n"
|
|
s += "\n"
|
|
|
|
if len(self.description) > 0:
|
|
s += f"{self.description}\n"
|
|
s += "\n"
|
|
|
|
if len(self.parameters) > 0:
|
|
s += f"- **Parameters:**\n"
|
|
for param in self.parameters:
|
|
s += f" - `{param.name}` ({param.type_})"
|
|
if len(param.description) > 0:
|
|
s += f": {param.description}\n"
|
|
else:
|
|
s += f"\n"
|
|
s += "\n"
|
|
|
|
if self.return_type is not None and len(self.return_type) > 0:
|
|
s += f"- **Returns:**\n"
|
|
s += f" - `{self.return_type}`: {self.return_description}\n"
|
|
|
|
s += "\n"
|
|
|
|
s += f"**Example Usage:**\n"
|
|
s += "```lua\n"
|
|
if self.return_type is not None and len(self.return_type) > 0:
|
|
s += self.return_type + " = "
|
|
|
|
if "Global Table" in prefix:
|
|
prefix = ""
|
|
s += f"{prefix}{self.name}({parameters_str})\n"
|
|
|
|
s += "```\n"
|
|
s += "\n"
|
|
|
|
return s
|
|
|
|
|
|
def make_table(table_name):
|
|
if table_name not in tables:
|
|
tables[table_name] = Table(table_name, [], [], "")
|
|
cur_table = tables[table_name]
|
|
return cur_table
|
|
|
|
|
|
def make_class(class_name):
|
|
if class_name not in classes:
|
|
classes[class_name] = Class(class_name, [], [], [], [], "")
|
|
cur_class = classes[class_name]
|
|
return cur_class
|
|
|
|
|
|
def is_comment_a_lua_api_doc_comment(text_lower):
|
|
return (
|
|
lua_api_comment_identifier in text_lower
|
|
and lua_api_comment_separator in text_lower
|
|
and "//" in text_lower
|
|
)
|
|
|
|
|
|
def parse_lua_api_doc(folder_path):
|
|
for root, dirs, files in os.walk(folder_path):
|
|
for file_name in files:
|
|
if os.path.splitext(file_name)[1].startswith((".c", ".h")):
|
|
file_path = os.path.join(root, file_name)
|
|
with open(file_path, "r") as file:
|
|
doc_kind = None
|
|
cur_table = None
|
|
cur_class = None
|
|
cur_function = None
|
|
cur_field = None
|
|
cur_constructor = None
|
|
|
|
for line in file:
|
|
line = line.strip()
|
|
line_lower = line.lower()
|
|
if is_comment_a_lua_api_doc_comment(line_lower):
|
|
doc_kind = DocKind(
|
|
line.split(lua_api_comment_separator, 1)[1]
|
|
.strip()
|
|
.lower()
|
|
)
|
|
|
|
if (
|
|
doc_kind is not DocKind.Tabs
|
|
and doc_kind is not DocKind.Infraction
|
|
):
|
|
continue
|
|
|
|
if doc_kind is not None and "//" in line:
|
|
match doc_kind:
|
|
case DocKind.Table:
|
|
cur_table = parse_table_doc(
|
|
cur_table, line, line_lower
|
|
)
|
|
case DocKind.Class:
|
|
cur_class = parse_class_doc(
|
|
cur_class, line, line_lower
|
|
)
|
|
case DocKind.Function:
|
|
(
|
|
cur_function,
|
|
cur_table,
|
|
cur_class,
|
|
) = parse_function_doc(
|
|
cur_function,
|
|
cur_table,
|
|
cur_class,
|
|
line,
|
|
line_lower,
|
|
)
|
|
case DocKind.Field:
|
|
(cur_field, cur_table, cur_class) = parse_field_doc(
|
|
cur_field,
|
|
cur_table,
|
|
cur_class,
|
|
line,
|
|
line_lower,
|
|
)
|
|
case DocKind.Constructor:
|
|
(
|
|
cur_constructor,
|
|
cur_class,
|
|
) = parse_constructor_doc(
|
|
cur_constructor,
|
|
cur_class,
|
|
line,
|
|
line_lower,
|
|
)
|
|
case DocKind.Tabs:
|
|
parse_tabs_doc(file)
|
|
case DocKind.Infraction:
|
|
parse_infraction_doc(file)
|
|
case _:
|
|
# print("unsupported doc kind: " + str(doc_kind))
|
|
pass
|
|
else:
|
|
doc_kind = None
|
|
|
|
|
|
def parse_table_doc(cur_table, line, line_lower):
|
|
if is_lua_doc_comment_startswith(line_lower, "name"):
|
|
table_name = line.split(lua_api_comment_separator, 1)[1].strip()
|
|
cur_table = make_table(table_name)
|
|
else:
|
|
if len(cur_table.description) != 0:
|
|
cur_table.description += "\n"
|
|
cur_table.description += sanitize_description(line)
|
|
|
|
return cur_table
|
|
|
|
|
|
def parse_class_doc(cur_class, line, line_lower):
|
|
if is_lua_doc_comment_startswith(line_lower, "name"):
|
|
class_name = line.split(lua_api_comment_separator, 1)[1].strip()
|
|
cur_class = make_class(class_name)
|
|
elif is_lua_doc_comment_startswith(line_lower, "inherit"):
|
|
inherited_class_name = line.split(lua_api_comment_separator, 1)[1].strip()
|
|
cur_class.inheritance.append(inherited_class_name)
|
|
else:
|
|
if len(cur_class.description) != 0:
|
|
cur_class.description += "\n"
|
|
cur_class.description += sanitize_description(line)
|
|
|
|
return cur_class
|
|
|
|
|
|
def parse_function_doc(cur_function, cur_table, cur_class, line, line_lower):
|
|
if (
|
|
is_lua_doc_comment_startswith(line_lower, "table")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
table_name = line.split(lua_api_comment_separator, 1)[1].strip()
|
|
cur_table = make_table(table_name)
|
|
|
|
cur_function = Function("Didnt get name yet", cur_table, [], None, "", "")
|
|
cur_table.functions.append(cur_function)
|
|
elif (
|
|
is_lua_doc_comment_startswith(line_lower, "class")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
class_name = line.split(lua_api_comment_separator, 1)[1].strip()
|
|
cur_class = make_class(class_name)
|
|
|
|
cur_function = Function("Didnt get name yet", cur_class, [], None, "", "")
|
|
cur_class.functions.append(cur_function)
|
|
elif (
|
|
is_lua_doc_comment_startswith(line_lower, "name")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
function_name = line.split(lua_api_comment_separator, 1)[1].strip()
|
|
cur_function.name = function_name
|
|
|
|
if function_name not in functions:
|
|
functions[function_name] = cur_function
|
|
elif (
|
|
is_lua_doc_comment_startswith(line_lower, "param")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
parameter = make_parameter_from_doc_line(line)
|
|
cur_function.parameters.append(parameter)
|
|
elif (
|
|
is_lua_doc_comment_startswith(line_lower, "return")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
return_info = line.split(lua_api_comment_separator, 2)
|
|
try:
|
|
cur_function.return_type = return_info[1].strip()
|
|
cur_function.return_description = return_info[2].strip()
|
|
except IndexError:
|
|
pass
|
|
else:
|
|
if len(cur_function.description) != 0:
|
|
cur_function.description += "\n"
|
|
cur_function.description += sanitize_description(line)
|
|
|
|
return cur_function, cur_table, cur_class
|
|
|
|
|
|
def parse_field_doc(cur_field, cur_table, cur_class, line, line_lower):
|
|
if (
|
|
is_lua_doc_comment_startswith(line_lower, "table")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
table_name = line.split(lua_api_comment_separator, 1)[1].strip()
|
|
cur_table = make_table(table_name)
|
|
|
|
cur_field = Field("Didnt get name yet", "", "")
|
|
cur_table.fields.append(cur_field)
|
|
elif (
|
|
is_lua_doc_comment_startswith(line_lower, "class")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
class_name = line.split(lua_api_comment_separator, 1)[1].strip()
|
|
cur_class = make_class(class_name)
|
|
|
|
cur_field = Field("Didnt get name yet", "", "")
|
|
cur_class.fields.append(cur_field)
|
|
elif (
|
|
is_lua_doc_comment_startswith(line_lower, "field")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
field_info = line.split(lua_api_comment_separator, 2)
|
|
cur_field.name = field_info[1].strip()
|
|
cur_field.type_ = field_info[2].strip()
|
|
else:
|
|
if len(cur_field.description) != 0:
|
|
cur_field.description += "\n"
|
|
|
|
if line.startswith("// "):
|
|
line = line[3:]
|
|
cur_field.description += sanitize_description(line)
|
|
|
|
return cur_field, cur_table, cur_class
|
|
|
|
|
|
def parse_constructor_doc(cur_constructor, cur_class, line, line_lower):
|
|
if (
|
|
is_lua_doc_comment_startswith(line_lower, "class")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
class_name = line.split(lua_api_comment_separator, 1)[1].strip()
|
|
cur_class = make_class(class_name)
|
|
|
|
cur_constructor = Constructor(cur_class, [], "")
|
|
cur_class.constructors.append(cur_constructor)
|
|
elif (
|
|
is_lua_doc_comment_startswith(line_lower, "param")
|
|
and lua_api_comment_separator in line_lower
|
|
):
|
|
parameter = make_parameter_from_doc_line(line)
|
|
cur_constructor.parameters.append(parameter)
|
|
else:
|
|
if len(cur_constructor.description) != 0:
|
|
cur_constructor.description += "\n"
|
|
cur_constructor.description += sanitize_description(line)
|
|
|
|
return cur_constructor, cur_class
|
|
|
|
|
|
tabs_enum = []
|
|
|
|
|
|
def parse_tabs_doc(file):
|
|
start_parsing = False
|
|
for line in file:
|
|
if "enum class" in line.lower():
|
|
start_parsing = True
|
|
continue
|
|
|
|
if start_parsing:
|
|
if "};" in line.lower():
|
|
return
|
|
if "{" == line.lower().strip():
|
|
continue
|
|
if "//" in line.lower():
|
|
continue
|
|
if "" == line.lower().strip():
|
|
continue
|
|
else:
|
|
tabs_enum.append(line.replace(",", "").strip())
|
|
|
|
|
|
infraction_enum = []
|
|
|
|
|
|
def parse_infraction_doc(file):
|
|
start_parsing = False
|
|
for line in file:
|
|
if "enum class" in line.lower():
|
|
start_parsing = True
|
|
continue
|
|
|
|
if start_parsing:
|
|
if "};" in line.lower():
|
|
return
|
|
if "{" == line.lower().strip():
|
|
continue
|
|
if "//" in line.lower():
|
|
continue
|
|
if "" == line.lower().strip():
|
|
continue
|
|
else:
|
|
infraction_enum.append(line.replace(",", "").strip())
|
|
|
|
|
|
def make_parameter_from_doc_line(line):
|
|
param_info = line.split(lua_api_comment_separator, 3)[1:]
|
|
param_name = param_type = param_desc = ""
|
|
|
|
try:
|
|
param_name = param_info[0].strip()
|
|
param_type = param_info[1].strip()
|
|
param_desc = param_info[2].strip()
|
|
except IndexError:
|
|
pass
|
|
|
|
return Parameter(param_name, param_type, param_desc)
|
|
|
|
|
|
def sanitize_description(line):
|
|
if line.startswith("// ") and line[3] != " ":
|
|
line = line[3:]
|
|
if line.startswith("//"):
|
|
line = line[2:]
|
|
return line.rstrip()
|
|
|
|
|
|
def is_lua_doc_comment_startswith(line_lower, starts_with_text):
|
|
return line_lower.replace("//", "").strip().startswith(starts_with_text)
|
|
|
|
|
|
parse_lua_api_doc(src_folder)
|
|
|
|
try:
|
|
os.makedirs("../docs/lua/tables/")
|
|
except:
|
|
pass
|
|
|
|
for table_name, table in tables.items():
|
|
file_name = f"../docs/lua/tables/{table_name}.md"
|
|
if os.path.exists(file_name):
|
|
os.remove(file_name)
|
|
f = open(file_name, "ba")
|
|
f.write(bytes(str(table), "UTF8"))
|
|
f.close()
|
|
|
|
tabs_file_name = f"../docs/lua/tabs.md"
|
|
if os.path.exists(tabs_file_name):
|
|
os.remove(tabs_file_name)
|
|
f = open(tabs_file_name, "a")
|
|
|
|
f.write(
|
|
"""# Tabs
|
|
|
|
All the tabs from the menu are listed below, used as parameter for adding gui elements to them.
|
|
|
|
**Example Usage:**
|
|
|
|
```lua
|
|
missionsTab = gui.get_tab("GUI_TAB_MISSIONS")
|
|
missionsTab:add_button("Click me", function ()
|
|
log.info("You clicked!")
|
|
end)
|
|
```
|
|
|
|
For a complete list of available gui functions, please refer to the tab class documentation and the gui table documentation.
|
|
|
|
"""
|
|
)
|
|
|
|
f.write(f"## Tab Count: {len(tabs_enum)}\n\n")
|
|
|
|
# Minus the first, because it's the `NONE` tab, minus the last one because it's for runtime defined tabs.
|
|
for i in range(1, len(tabs_enum) - 1):
|
|
f.write("### `GUI_TAB_" + tabs_enum[i] + "`\n")
|
|
|
|
f.close()
|
|
|
|
infraction_file_name = f"../docs/lua/infraction.md"
|
|
if os.path.exists(infraction_file_name):
|
|
os.remove(infraction_file_name)
|
|
f = open(infraction_file_name, "a")
|
|
|
|
f.write(
|
|
"""# Infraction
|
|
|
|
All the infraction from the menu are listed below, used as parameter for adding an infraction to a given player, for flagging them as modder.
|
|
|
|
**Example Usage:**
|
|
```lua
|
|
network.flag_player_as_modder(player_index, infraction.CUSTOM_REASON, "My custom reason on why the player is flagged as a modder")
|
|
```
|
|
|
|
"""
|
|
)
|
|
|
|
f.write(f"## Infraction Count: {len(infraction_enum)}\n\n")
|
|
|
|
for i in range(0, len(infraction_enum)):
|
|
f.write("### `" + infraction_enum[i] + "`\n")
|
|
|
|
f.close()
|
|
|
|
try:
|
|
os.makedirs("../docs/lua/classes/")
|
|
except:
|
|
pass
|
|
|
|
for class_name, class_ in classes.items():
|
|
file_name = f"../docs/lua/classes/{class_name}.md"
|
|
if os.path.exists(file_name):
|
|
os.remove(file_name)
|
|
f = open(file_name, "ba")
|
|
f.write(bytes(str(class_), "UTF8"))
|
|
f.close()
|
|
|
|
|
|
commands_file_name = f"../docs/lua/commands.md"
|
|
if os.path.exists(commands_file_name):
|
|
os.remove(commands_file_name)
|
|
f = open(commands_file_name, "a")
|
|
|
|
f.write(
|
|
"""# Commands
|
|
|
|
All the current commands from the menu are listed below.
|
|
|
|
**Example Usage through Lua:**
|
|
|
|
```lua
|
|
command.call("spawn", {joaat("adder")})
|
|
command.call_player(somePlayerIndex, "spawn", {joaat("adder")})
|
|
```
|
|
|
|
For a complete list of available command functions, please refer to the command table documentation.
|
|
|
|
"""
|
|
)
|
|
|
|
|
|
commands = []
|
|
with open("../docs/lua/commands_dump.txt", "r") as file:
|
|
for line in file:
|
|
cmd = line.split("|", 1)[1].strip().split("|")
|
|
commands.append(cmd)
|
|
|
|
f.write(f"## Command Count: {len(commands)}\n\n")
|
|
|
|
for cmd in commands:
|
|
name = cmd[0]
|
|
label = cmd[1]
|
|
desc = cmd[2]
|
|
arg_count = cmd[3]
|
|
f.write(f"### {name}\n")
|
|
f.write(f"{desc}\n")
|
|
f.write(f"Arg Count: {arg_count}\n")
|
|
f.write("\n")
|
|
|
|
f.close()
|