# SPDX-License-Identifier: BSD-2-Clause """ This module provides classes for Sphinx content generation. """ # Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de) # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. from contextlib import contextmanager from typing import Any, Iterable, Iterator, List, Optional, Sequence, Union from rtemsspec.content import Content, make_lines, to_camel_case from rtemsspec.items import Item, ItemGetValueContext, ItemMapper GenericContent = Union[str, List[str], "Content"] GenericContentIterable = Union[Iterable[str], Iterable[List[str]], Iterable[GenericContent]] _HEADER_LEVELS = ["#", "*", "=", "-", "^", "\""] def get_reference(label: str, name: Optional[str] = None) -> str: """ Returns the reference to the specified label. """ if name: return f":ref:`{name} <{label}>`" return f":ref:`{label}`" def get_label(name: str) -> str: """ Returns the label for the specified name. """ return to_camel_case(name.strip()) def _simple_sep(maxi: Iterable[int]) -> str: return " ".join(f"{'=' * val}" for val in maxi) def _simple_row(row: Iterable[str], maxi: Iterable[int]) -> str: line = " ".join("{0:{width}}".format(cell, width=val) for cell, val in zip(row, maxi)) return line.rstrip() class SphinxContent(Content): """ This class builds Sphinx content. """ def __init__(self, section_level: int = 2): super().__init__("CC-BY-SA-4.0", True) self._tab = " " self._section_level = section_level self.section_label_prefix = "Section" def add_label(self, label: str) -> None: """ Adds a label. """ self.add(".. _" + label.strip() + ":") def add_header(self, name, level=2) -> None: """ Adds a header. """ name = name.strip() self.add([name, _HEADER_LEVELS[level] * len(name)]) def add_header_with_label(self, name: str, level: int = 2, label_prefix: Optional[str] = None) -> str: """ Adds a header with label. """ if label_prefix is None: label_prefix = self.section_label_prefix label = label_prefix + get_label(name) self.add_label(label) self.add_header(name, level) return label def add_index_entries(self, entries) -> None: """ Adds a list of index entries the content. """ self.add([".. index:: " + entry for entry in make_lines(entries)]) def add_definition_item(self, name: GenericContent, definition: GenericContent, wrap: bool = False) -> None: """ Adds a definition item the content. """ @contextmanager def _definition_item_context(content: Content) -> Iterator[None]: content.append(name) content.push_indent() yield content.pop_indent() if wrap: self.wrap(definition, context=_definition_item_context) else: self.add(definition, context=_definition_item_context) @contextmanager def definition_item(self, name: GenericContent) -> Iterator[None]: """ Opens a definition item context. """ self.wrap(name) self.push_indent() yield self.pop_indent() def open_directive(self, name: str, value: Optional[str] = None, options: Optional[List[str]] = None) -> None: """ Opens a directive. """ value = " " + value if value else "" self.add(f".. {name.strip()}::{value}") self.push_indent() self.add(options) self.gap = True def close_directive(self) -> None: """ Closes a directive. """ self.pop_indent() @contextmanager def directive(self, name: str, value: Optional[str] = None, options: Optional[List[str]] = None): """ Opens a directive context. """ self.open_directive(name, value, options) yield self.close_directive() def open_section(self, name: str, label_prefix: Optional[str] = None) -> str: """ Opens a section. """ label = self.add_header_with_label(name, self._section_level, label_prefix) self._section_level += 1 return label def close_section(self) -> None: """ Closes a section. """ self._section_level -= 1 @contextmanager def section(self, name: str, label_prefix: Optional[str] = None) -> Iterator[str]: """ Opens a section context. """ yield self.open_section(name, label_prefix) self.close_section() def add_licence_and_copyrights(self) -> None: """ Adds a licence and copyright block according to the registered licenses and copyrights. """ statements = self._copyrights.get_statements() if statements: self.prepend("") self.prepend([f".. {stm}" for stm in statements]) self.prepend([f".. SPDX-License-Identifier: {self._license}", ""]) def open_comment_block(self) -> None: """ Opens a comment block. """ self.push_indent(".. ", "..") def add_simple_table(self, rows: Sequence[Iterable[str]]) -> None: """ Adds a simple table. """ if not rows: return maxi = tuple(map(len, rows[0])) for row in rows: row_lengths = tuple(map(len, row)) maxi = tuple(map(max, zip(maxi, row_lengths))) sep = _simple_sep(maxi) lines = [sep, _simple_row(rows[0], maxi), sep] lines.extend(_simple_row(row, maxi) for row in rows[1:]) lines.append(sep) with self.directive("table", options=[":class: longtable"]): self.add(lines) def _get_ref_term(ctx: ItemGetValueContext) -> Any: return f":term:`{ctx.value[ctx.key]}`" def _get_ref_term_plural(ctx: ItemGetValueContext) -> Any: try: return f":term:`{ctx.value[ctx.key]} <{ctx.value['term']}>`" except KeyError: return f":term:`{ctx.value['term']}s <{ctx.value['term']}>`" def _get_appl_config_option(ctx: ItemGetValueContext) -> Any: return f":ref:`{ctx.value[ctx.key]}`" def _get_value_sphinx_macro(ctx: ItemGetValueContext) -> Any: return f":c:macro:`{ctx.value[ctx.key]}`" def _get_value_sphinx_function(ctx: ItemGetValueContext) -> Any: return f":c:func:`{ctx.value[ctx.key]}`" def _get_value_sphinx_type(ctx: ItemGetValueContext) -> Any: return f":c:type:`{ctx.value[ctx.key]}`" def _get_value_sphinx_url(ctx: ItemGetValueContext) -> Any: return f"`{ctx.value[ctx.key]} <{ctx.item['reference']}>`_" def _get_value_sphinx_unspecified_define(ctx: ItemGetValueContext) -> Any: if ctx.item["reference"]: return _get_value_sphinx_url(ctx) return _get_value_sphinx_macro(ctx) def _get_value_sphinx_unspecified_type(ctx: ItemGetValueContext) -> Any: if ctx.item["reference"]: return _get_value_sphinx_url(ctx) return _get_value_sphinx_type(ctx) class SphinxMapper(ItemMapper): """ Sphinx item mapper. """ def __init__(self, item: Item, recursive: bool = False): super().__init__(item, recursive) self.add_get_value("glossary/term:/term", _get_ref_term) self.add_get_value("glossary/term:/plural", _get_ref_term_plural) self.add_get_value("interface/appl-config-option/feature-enable:/name", _get_appl_config_option) self.add_get_value("interface/appl-config-option/feature:/name", _get_appl_config_option) self.add_get_value("interface/appl-config-option/initializer:/name", _get_appl_config_option) self.add_get_value("interface/appl-config-option/integer:/name", _get_appl_config_option) self.add_get_value("interface/define:/name", _get_value_sphinx_macro) self.add_get_value("interface/enum:/name", _get_value_sphinx_type) self.add_get_value("interface/enumerator:/name", _get_value_sphinx_macro) self.add_get_value("interface/function:/name", _get_value_sphinx_function) self.add_get_value("interface/macro:/name", _get_value_sphinx_function) self.add_get_value("interface/struct:/name", _get_value_sphinx_type) self.add_get_value("interface/typedef:/name", _get_value_sphinx_type) self.add_get_value("interface/union:/name", _get_value_sphinx_type) self.add_get_value("interface/unspecified-define:/name", _get_value_sphinx_unspecified_define) self.add_get_value("interface/unspecified-function:/name", _get_value_sphinx_function) self.add_get_value("interface/unspecified-type:/name", _get_value_sphinx_unspecified_type)