Files
gnatstudio/docs/users_guide/generate.py
2024-06-06 12:20:22 +00:00

348 lines
11 KiB
Python

import inspect
import GPS
from gs_utils.internal.utils import dump_menu, run_test_driver, timeout
class Inspect(object):
"""An inspector for a given module"""
module_header = """Scripting API reference for `%(module)s`
==========================================
.. automodule:: %(module)s
.. inheritance-diagram GPS # DISABLED, add "::" to enable
"""
functions_header = """
Functions
---------
"""
function_stub = ".. autofunction:: %(name)s\n"
classes_header = """
Classes
-------
"""
class_stub = """
:class:`%(module)s.%(name)s`
%(underscore)s
.. autoclass:: %(name)s()
%(members)s
%(inheritance)s
"""
method_stub = """
.. automethod:: %(name)s
"""
data_stub = """
.. autoattribute:: %(name)s
"""
exceptions_header = """
Exceptions
-------
"""
def __init__(self, module):
self.module = module
self.func = []
self.classes = []
self.excepts = []
for obj_name, obj in module.__dict__.items():
if obj_name.startswith("__") and obj_name not in ["__init__"]:
pass
elif inspect.ismodule(obj):
pass
elif inspect.isfunction(obj) or inspect.isroutine(obj):
self.func.append(obj_name)
elif isinstance(obj, Exception):
self.excepts.append(obj_name)
elif inspect.isclass(obj):
self.classes.append((obj_name, obj))
self.func.sort()
self.excepts.sort()
self.classes.sort()
def __methods(self, cls):
"""Returns the methods and data of the class"""
methods = []
data = []
for name, kind, defined, obj in inspect.classify_class_attrs(cls):
if defined != cls:
pass # Inherited method
elif name.startswith("_") and name not in ["__init__"]:
pass
elif kind in ["method", "static method", "class method"]:
methods.append(name)
elif kind in ["property", "data"]:
data.append(name)
else:
print("Unknown kind (%s) for %s.%s" % (kind, defined.__name__, name))
methods.sort()
data.sort()
return (data, methods)
def generate_rest(self):
"""Generate a REST file for the given module.
The output should be processed by sphinx.
"""
n = self.module.__name__
fd = open("%s.rst" % n, "w", newline="\n")
fd.write(".. This file is automatically generated, do not edit\n\n")
fd.write(Inspect.module_header % {"module": n})
if self.func:
fd.write(Inspect.functions_header)
for f in self.func:
fd.write(Inspect.function_stub % {"name": f, "module": n})
if self.classes:
fd.write(Inspect.classes_header)
for name, c in self.classes:
# Only show inheritance diagram if base classes are other
# than just "object"
inheritance = ""
mro = inspect.getmro(c) # first member is always c
if len(mro) > 2 or (len(mro) == 2 and mro[1].__name__ != "object"):
inheritance = " .. inheritance-diagram:: %s.%s" % (n, name)
if name in (
"FileContext",
"AreaContext",
"MessageContext",
"EntityContext",
):
# These are for backward compatibility only
continue
fd.write(
Inspect.class_stub
% {
"name": name,
"inheritance": inheritance,
"members": "",
"underscore": "^" * (len(name) + len(n) + 10),
"module": n,
}
)
data, methods = self.__methods(c)
for d in data:
mname = "%s.%s.%s" % (n, name, d)
fd.write(
Inspect.data_stub
% {
"name": mname,
"base_name": d,
"underscore": "*" * (len(d) + 8),
}
)
for m in methods:
mname = "%s.%s.%s" % (n, name, m)
fd.write(
Inspect.method_stub
% {
"name": mname,
"base_name": m,
"inheritance": " .. inheritance-diagram:: %s.%s" % (n, c),
"underscore": "*" * (len(m) + 8),
}
)
if name == "Hook":
# Include generated doc for predefined hooks
fd.write(
Inspect.class_stub
% {
"name": "Predefined_Hooks",
"inheritance": "",
"members": " :members:\n",
"underscore": "^" * (len(n) + 10 + 16),
"module": n,
}
)
if self.excepts:
fd.write(Inspect.exceptions_header)
for c in self.excepts:
fd.write(
Inspect.class_stub
% {
"name": c,
"inheritance": ".. inheritance-diagram:: %s.%s" % (n, c),
"underscore": "^" * (len(c) + len(n) + 10),
"module": n,
}
)
# (re)generate the Python API doc
Inspect(GPS).generate_rest()
Inspect(GPS.Browsers).generate_rest()
# (re)generate the menus doc
toplevel_menus = [
"File",
"Edit",
"Navigate",
"Find",
"View",
"Code",
"VCS",
"Build",
"Analyze",
"Debug",
"SPARK",
"CodePeer",
"GNATSAS",
"Window",
"Help",
]
menu_specialcases = {
"/SPARK": """This menu is available if the SPARK toolset is installed on your system
and available on your PATH. See :menuselection:`Help --> SPARK -->
Reference --> Using SPARK with GPS` for more details.
""",
"/CodePeer": """This menu is available if the CodePeer toolset is installed on your
system and available on your PATH. See your CodePeer documentation for
more details.
""",
"/Help/CodePeer": """This menu is available if the CodePeer toolset is installed on your
system and available on your PATH. See your CodePeer documentation for
more details.
""",
"/GNATSAS": """This menu is available if the GNATSAS toolset is installed on your
system and available on your PATH. See your GNATSAS documentation for
more details.
""",
"/Help/GNATSAS": """This menu is available if the GNATSAS toolset is installed on your
system and available on your PATH. See your GNATSAS documentation for
more details.
""",
"/Help/GNAT Runtime": """This menu is generated automatically, and provides
pointers to the contents of the currently loaded runtime.
""",
"/Help/GNAT": """This menu is generated automatically, and provides documentation
for the compiler and accompanying tools, along with examples.
""",
"/Help/GNU Tools": """Manuals for the GNU Tools that come with your compiler.
""",
"/Help/Ada": """This menu contains pointers to the Ada reference manuals.
""",
"/File/Open Recent Projects": """Use this menu to load one of the recently open projects.
""",
"/Build/Bareboard": """Actions to build bareboard code.
""",
"/Build/Makefile": """Actions to launch Makefile targets - this is generated
automatically when a Makefile is present next to the loaded project.
""",
}
menu_file_header = """
.. This file is automatically generated by generate.py, from the contents
.. of the menu actions.
.. index:: menu; menus
************
The Menu Bar
************
.. image:: menubar.png
GPS provides a standard menu bar giving access to all operations. However,
it is usually easier to access a feature using the various contextual menus
provided throughout GPS: these give direct access to the most relevant
actions in the current context (for example, a project, directory, file, or
entity). Contextual menus pop up when you click the right mouse button or
use the special :kbd:`open contextual menu` key on most keyboards.
You can access the following entries from the menu bar:
"""
def print_menu_header(f, menupath, indent):
textpath = menupath[1:].replace("/", " --> ")
f.write(indent + ".. index:: menu; {}\n\n".format(textpath.lower()))
f.write(indent + "* :menuselection:`{}`\n\n".format(textpath))
def print_entry(f, menupath, indent):
print("generating menu doc {}".format(menupath))
print_menu_header(f, menupath, indent)
for line in GPS.Menu.get(menupath).action.__doc__.splitlines():
if line:
f.write(indent + " " + "{}\n".format(line))
else:
f.write("\n")
def print_menu(f, lis, parent_path, indent):
"""Print in file f the menu represented by list lis, at the given indent"""
if parent_path in menu_specialcases:
print_menu_header(f, parent_path, indent)
for line in menu_specialcases[parent_path].splitlines():
f.write(indent + " " + line + "\n")
f.write("\n")
else:
lis.append("")
prev_entry = None
for entry in lis:
if entry != "<separator>":
if type(prev_entry) == str:
menupath = parent_path + "/" + prev_entry
if type(entry) == str:
print_entry(f, menupath, indent)
else:
print_menu(f, entry, menupath, indent)
prev_entry = entry
@run_test_driver
def driver():
while len(dump_menu("/File/Open Recent Projects")) == 0:
yield timeout(100)
with open("menus.rst", "w", newline="\n") as f:
f.write(menu_file_header)
# Generate the indexes
for m in toplevel_menus:
f.write(" * :menuselection:`{}` (see :ref:`The_{}_Menu`)\n\n".format(m, m))
# Generate the menu contents
for m in toplevel_menus:
f.write(".. index:: {}\n\n".format(m.lower()))
f.write(".. _The_{}_Menu:\n\n".format(m))
title = "The {} Menu".format(m)
f.write("{}\n{}\n\n".format(title, "=" * len(title)))
parent_path = "/{}".format(m)
menu_list = dump_menu(parent_path) + ["", ""]
print_menu(f, menu_list[1], parent_path, "")