You've already forked gnatstudio
mirror of
https://github.com/AdaCore/gnatstudio.git
synced 2026-02-12 12:42:33 -08:00
348 lines
11 KiB
Python
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, "")
|