Files
OpenRA/packaging/format-docs.py
2023-01-11 11:58:54 +02:00

128 lines
5.8 KiB
Python

#!/usr/bin/env python3
# Copyright (c) The OpenRA Developers and Contributors
# This file is part of OpenRA, which is free software. It is made
# available to you under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version. For more
# information, see COPYING.
import io
import sys
import json
from collections import OrderedDict
def format_type_name(typeName, isKnownType):
name = typeName
if name.endswith("Info"):
name = name[0:-4]
return f'[`{name}`](#{name.lower()})' if isKnownType else f'`{name}`'
def is_known_type(typeName, types):
name = typeName
if name.endswith("Info"):
name = name[0:-4]
result = [t for t in types if name == t["Name"]]
return len(result) > 0
def format_docs(version, collectionName, types, relatedEnums):
typesByNamespace = OrderedDict()
for currentType in types:
if currentType["Namespace"] in typesByNamespace:
typesByNamespace[currentType["Namespace"]].append(currentType)
else:
typesByNamespace[currentType["Namespace"]] = [currentType]
# Map the `relatedEnums` collection to a list of strings.
enumNames = [enum['Name'] for enum in relatedEnums]
enumReferences = OrderedDict()
title = ""
explanation = ""
if collectionName == "TraitInfos":
title = "Traits"
explanation = "all traits with their properties and their default values plus developer commentary"
elif collectionName == "WeaponTypes":
title = "Weapons"
explanation = "a template for weapon definitions and the types it can use (warheads and projectiles) with default values and developer commentary"
elif collectionName == "SpriteSequenceTypes":
title = "Sprite sequences"
explanation = "all sprite sequence types with their properties and their default values plus developer commentary"
print(f"# {title}\n")
print(f"This documentation is aimed at modders and has been automatically generated for version `{version}` of OpenRA. " +
"Please do not edit it directly, but instead add new `[Desc(\"String\")]` tags to the source code.\n")
print(f"Listed below are {explanation}.")
print(f"Related types with their possible values are listed [at the bottom](#related-value-types-enums).")
for namespace in typesByNamespace:
print(f'\n## {namespace}')
for currentType in typesByNamespace[namespace]:
print(f'\n### {currentType["Name"]}')
if currentType["Description"]:
print(f'**{currentType["Description"]}**')
if "InheritedTypes" in currentType and currentType["InheritedTypes"]:
inheritedTypes = [t for t in currentType["InheritedTypes"] if t not in ['TraitInfo', 'Warhead']] # Remove blacklisted types.
if inheritedTypes:
print("\n> Inherits from: " + ", ".join([format_type_name(x, is_known_type(x, types)) for x in inheritedTypes]) + '.')
if "RequiresTraits" in currentType and currentType["RequiresTraits"]:
formattedRequiredTraits = [format_type_name(x, is_known_type(x, types)) for x in currentType["RequiresTraits"]]
print("\n> Requires trait(s): " + ", ".join(sorted(formattedRequiredTraits)) + '.')
if len(currentType["Properties"]) > 0:
print()
print(f'| Property | Default Value | Type | Description |')
print(f'| -------- | ------------- | ---- | ----------- |')
for prop in currentType["Properties"]:
# Use the user-friendly type name unless we're certain this is a known enum,
# in which case get a link to the enum's definition.
typeName = prop["UserFriendlyType"]
if prop["InternalType"] in enumNames:
typeName = format_type_name(prop["InternalType"], True)
if prop["InternalType"] in enumReferences:
enumReferences[prop["InternalType"]].append(currentType["Name"])
else:
enumReferences[prop["InternalType"]] = [currentType["Name"]]
if "OtherAttributes" in prop:
attributes = []
for attribute in prop["OtherAttributes"]:
attributes.append(attribute["Name"])
defaultValue = ''
if prop["DefaultValue"]:
defaultValue = prop["DefaultValue"]
elif 'Require' in attributes:
defaultValue = '*(required)*'
print(f'| {prop["PropertyName"]} | {defaultValue} | {typeName} | {prop["Description"]} |')
else:
print(f'| {prop["PropertyName"]} | {prop["DefaultValue"] or ""} | {typeName} | {prop["Description"]} |')
if len(relatedEnums) > 0:
print('\n# Related value types (enums):\n')
for relatedEnum in relatedEnums:
values = [f"`{value['Value']}`" for value in relatedEnum["Values"]]
print(f"### {relatedEnum['Name']}")
print(f"Possible values: {', '.join(values)}\n")
distinctReferencingTypes = sorted(set(enumReferences[relatedEnum['Name']]))
formattedReferencingTypes = [format_type_name(x, is_known_type(x, types)) for x in distinctReferencingTypes]
print(f"Referenced by: {', '.join(formattedReferencingTypes)}\n")
if __name__ == "__main__":
input_stream = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8-sig')
jsonInfo = json.load(input_stream)
keys = list(jsonInfo)
if len(keys) == 3 and keys[0] == 'Version':
format_docs(jsonInfo[keys[0]], keys[1], jsonInfo[keys[1]], jsonInfo[keys[2]])