blob: dfd211fab00a81dcb7d439653d1f1e631f13f15f [file] [log] [blame]
import re
from pathlib import Path
from .generatorbase import GeneratorBase
from .model import (
ReferenceManual,
Function,
Object,
PosArg,
VarArgs,
Kwarg,
)
import typing as T
class ManPage:
def __init__(self, path: Path):
self.path = path
self.text = ""
def reset_font(self) -> None:
self.text += ".P\n"
def title(self, name: str, section: int) -> None:
import datetime
date = datetime.date.today()
self.reset_font()
self.text += f'.TH "{name}" "{section}" "{date}"\n'
def section(self, name: str) -> None:
self.reset_font()
self.text += f".SH {name}\n"
def subsection(self, name: str) -> None:
self.reset_font()
self.text += f".SS {name}\n"
def par(self, text: str) -> None:
self.reset_font()
self.text += f"{text}\n"
def indent(self, amount: int = 4) -> None:
self.text += f".RS {amount}\n"
def unindent(self) -> None:
self.text += ".RE\n"
def br(self) -> None:
self.text += ".br\n"
def nl(self) -> None:
self.text += "\n"
def line(self, text: str) -> None:
if text and text[0] in [".", "'"]:
self.text += "\\"
self.text += f"{text}\n"
def inline(self, text: str) -> None:
self.text += f"{text}"
def write(self) -> None:
self.path.write_text(self.text, encoding="utf-8")
@staticmethod
def bold(text: str) -> str:
return f"\\fB{text}\\fR"
@staticmethod
def italic(text: str) -> str:
return f"\\fI{text}\\fR"
class GeneratorMan(GeneratorBase):
def __init__(
self, manual: ReferenceManual, out: Path, enable_modules: bool
) -> None:
super().__init__(manual)
self.out = out
self.enable_modules = enable_modules
self.links: T.List[str] = []
def generate_description(self, page: ManPage, desc: str) -> None:
def italicise(match: T.Match[str]) -> str:
v = match.group(1)
if v[0] == "@":
v = v[1:]
return ManPage.italic(v)
desc = re.sub(re.compile(r"\[\[(.*?)\]\]", re.DOTALL), italicise, desc)
def linkify(match: T.Match[str]) -> str:
replacement = ManPage.italic(match.group(1))
if match.group(2)[0] != "#":
if match.group(2) in self.links:
num = self.links.index(match.group(2))
else:
self.links.append(match.group(2))
num = len(self.links)
replacement += f"[{num}]"
return replacement
desc = re.sub(re.compile(r"\[(.*?)\]\((.*?)\)", re.DOTALL), linkify, desc)
def bold(match: T.Match[str]) -> str:
return ManPage.bold(match.group(1))
desc = re.sub(re.compile(r"\*(.*?)\*"), bold, desc)
isCode = False
for chunk in desc.split("```"):
if isCode:
page.indent()
lines = chunk.strip().split("\n")
if lines[0] == "meson":
lines = lines[1:]
for line in lines:
page.line(line)
page.br()
page.unindent()
else:
inList = False
for line in chunk.strip().split("\n"):
if len(line) == 0:
page.nl()
if inList:
page.nl()
inList = False
elif line[0:2] in ["- ", "* "]:
if inList:
page.nl()
page.br()
else:
inList = True
page.inline(line.strip() + " ")
elif inList and line[0] == " ":
page.inline(line.strip() + " ")
else:
inList = False
page.line(line)
if inList:
page.nl()
isCode = not isCode
def function_name(self, f: Function, o: Object = None) -> str:
name = ""
if o is not None:
name += f"{o.name}."
name += f.name
return name
def generate_function_signature(
self, page: ManPage, f: Function, o: Object = None
) -> None:
args = []
if f.posargs:
args += [arg.name for arg in f.posargs]
if f.varargs:
args += [f.varargs.name + "..."]
if f.optargs:
args += [f"[{arg.name}]" for arg in f.optargs]
for kwarg in self.sorted_and_filtered(list(f.kwargs.values())):
kw = kwarg.name + ":"
if kwarg.default:
kw += " " + ManPage.bold(kwarg.default)
args += [kw]
ret = ManPage.italic(f.returns.raw) + " "
prefix = f"{ret}{self.function_name(f, o)}("
sig = ", ".join(args)
suffix = ")"
if len(prefix) + len(sig) + len(suffix) > 70:
page.line(prefix)
page.br()
page.indent()
for arg in args:
page.line(arg + ",")
page.br()
page.unindent()
page.line(suffix)
else:
page.line(prefix + sig + suffix)
def base_info(
self, x: T.Union[PosArg, VarArgs, Kwarg, Function, Object]
) -> T.List[str]:
info = []
if x.deprecated:
info += [ManPage.bold("deprecated") + f" since {x.deprecated}"]
if x.since:
info += [f"since {x.since}"]
return info
def generate_function_arg(
self,
page: ManPage,
arg: T.Union[PosArg, VarArgs, Kwarg],
isOptarg: bool = False,
) -> None:
required = (
arg.required
if isinstance(arg, Kwarg)
else not isOptarg and not isinstance(arg, VarArgs)
)
page.line(ManPage.bold(arg.name))
info = [ManPage.italic(arg.type.raw)]
if required:
info += [ManPage.bold("required")]
if isinstance(arg, (PosArg, Kwarg)) and arg.default:
info += [f"default: {arg.default}"]
if isinstance(arg, VarArgs):
mn = 0 if arg.min_varargs < 0 else arg.min_varargs
mx = "N" if arg.max_varargs < 0 else arg.max_varargs
info += [f"{mn}...{mx} times"]
info += self.base_info(arg)
page.line(", ".join(info))
page.br()
page.indent(2)
self.generate_description(page, arg.description.strip())
page.unindent()
page.nl()
def generate_function_argument_section(
self,
page: ManPage,
name: str,
args: T.Sequence[T.Union[PosArg, VarArgs, Kwarg]],
isOptarg: bool = False,
) -> None:
if not args:
return
page.line(ManPage.bold(name))
page.indent()
for arg in args:
self.generate_function_arg(page, arg, isOptarg)
page.unindent()
def generate_sub_sub_section(
self, page: ManPage, name: str, text: T.List[str], process: bool = True
) -> None:
page.line(ManPage.bold(name))
page.indent()
if process:
for line in text:
self.generate_description(page, line.strip())
else:
page.line("\n\n".join([line.strip() for line in text]))
page.unindent()
def generate_function(self, page: ManPage, f: Function, obj: Object = None) -> None:
page.subsection(self.function_name(f, obj) + "()")
page.indent(0)
page.line(ManPage.bold("SYNOPSIS"))
page.indent()
self.generate_function_signature(page, f, obj)
info = self.base_info(f)
if info:
page.nl()
page.line(", ".join(info))
page.unindent()
page.nl()
self.generate_sub_sub_section(page, "DESCRIPTION", [f.description])
page.nl()
self.generate_function_argument_section(page, "POSARGS", f.posargs)
if f.varargs:
self.generate_function_argument_section(page, "VARARGS", [f.varargs])
self.generate_function_argument_section(page, "OPTARGS", f.optargs, True)
self.generate_function_argument_section(
page, "KWARGS", self.sorted_and_filtered(list(f.kwargs.values()))
)
if f.notes:
self.generate_sub_sub_section(page, "NOTES", f.notes)
if f.warnings:
self.generate_sub_sub_section(page, "WARNINGS", f.warnings)
if f.example:
self.generate_sub_sub_section(page, "EXAMPLE", [f.example])
page.unindent()
def generate_object(self, page: ManPage, obj: Object) -> None:
page.subsection(obj.name)
page.indent(2)
info = self.base_info(obj)
if info:
page.line(", ".join(info))
page.br()
if obj.extends:
page.line(ManPage.bold("extends: ") + obj.extends)
page.br()
ret = [x.name for x in self.sorted_and_filtered(obj.returned_by)]
if ret:
page.line(ManPage.bold("returned_by: ") + ", ".join(ret))
page.br()
ext = [x.name for x in self.sorted_and_filtered(obj.extended_by)]
if ext:
page.line(ManPage.bold("extended_by: ") + ", ".join(ext))
page.br()
page.nl()
self.generate_description(page, obj.description.strip())
page.nl()
if obj.notes:
self.generate_sub_sub_section(page, "NOTES", obj.notes)
if obj.warnings:
self.generate_sub_sub_section(page, "WARNINGS", obj.warnings)
if obj.example:
self.generate_sub_sub_section(page, "EXAMPLE", [obj.example])
page.unindent()
def generate(self) -> None:
page = ManPage(self.out)
page.title("meson-reference", 3)
page.section("NAME")
page.par(
f"meson-reference v{self._extract_meson_version()}"
+ " - a reference for meson functions and objects"
)
page.section("DESCRIPTION")
self.generate_description(
page,
"""This manual is divided into two sections, *FUNCTIONS* and *OBJECTS*. *FUNCTIONS* contains a reference for all meson functions and methods. Methods are denoted by [[object_name]].[[method_name]](). *OBJECTS* contains additional information about each object.""",
)
page.section("FUNCTIONS")
for f in self.sorted_and_filtered(self.functions):
self.generate_function(page, f)
for obj in self.sorted_and_filtered(self.objects):
for f in self.sorted_and_filtered(obj.methods):
self.generate_function(page, f, obj)
page.section("OBJECTS")
for obj in self.sorted_and_filtered(self.objects):
self.generate_object(page, obj)
page.section("SEE ALSO")
for i in range(len(self.links)):
link = self.links[i]
page.line(f"[{i + 1}] {link}")
page.br()
page.write()