source: rtems-central/rtemsspec/sphinxcontent.py @ d9fa7d6

Last change on this file since d9fa7d6 was e49c759, checked in by Sebastian Huber <sebastian.huber@…>, on 07/15/20 at 08:04:25

Rename "rtemsqual" in "rtemsspec"

  • Property mode set to 100644
File size: 8.7 KB
Line 
1# SPDX-License-Identifier: BSD-2-Clause
2""" This module provides classes for Sphinx content generation. """
3
4# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25# POSSIBILITY OF SUCH DAMAGE.
26
27from contextlib import contextmanager
28import re
29from typing import Any, Iterable, Iterator, List, Optional, Union
30
31from rtemsspec.content import Content, make_lines
32from rtemsspec.items import Item, ItemGetValueContext, ItemMapper
33
34GenericContent = Union[str, List[str], "Content"]
35GenericContentIterable = Union[Iterable[str], Iterable[List[str]],
36                               Iterable[GenericContent]]
37
38_HEADER_LEVELS = ["#", "*", "=", "-", "^", "\""]
39
40
41def _to_camel_case(name: str) -> str:
42    return name[0].upper() + re.sub(
43        r"\s+(.)", lambda match: match.group(1).upper(),
44        re.sub(r"[^ \t\n\r\f\va-zA-Z0-9]", " ", name[1:].replace("+", "X")))
45
46
47def get_reference(label: str, name: Optional[str] = None) -> str:
48    """ Returns the reference to the specified label. """
49    if name:
50        return f":ref:`{name} <{label}>`"
51    return f":ref:`{label}`"
52
53
54def get_label(name: str) -> str:
55    """ Returns the label for the specified name. """
56    return _to_camel_case(name.strip())
57
58
59class SphinxContent(Content):
60    """ This class builds Sphinx content. """
61    def __init__(self, section_level: int = 2):
62        super().__init__("CC-BY-SA-4.0", True)
63        self._tab = "    "
64        self._section_level = section_level
65        self.section_label_prefix = "Section"
66
67    def add_label(self, label: str) -> None:
68        """ Adds a label. """
69        self.add(".. _" + label.strip() + ":")
70
71    def add_header(self, name, level=2) -> None:
72        """ Adds a header. """
73        name = name.strip()
74        self.add([name, _HEADER_LEVELS[level] * len(name)])
75
76    def add_header_with_label(self,
77                              name: str,
78                              level: int = 2,
79                              label_prefix: Optional[str] = None) -> str:
80        """ Adds a header with label. """
81        if label_prefix is None:
82            label_prefix = self.section_label_prefix
83        label = label_prefix + get_label(name)
84        self.add_label(label)
85        self.add_header(name, level)
86        return label
87
88    def add_index_entries(self, entries) -> None:
89        """ Adds a list of index entries the content. """
90        self.add([".. index:: " + entry for entry in make_lines(entries)])
91
92    def add_definition_item(self,
93                            name: GenericContent,
94                            definition: GenericContent,
95                            wrap: bool = False) -> None:
96        """ Adds a definition item the content. """
97        @contextmanager
98        def _definition_item_context(content: Content) -> Iterator[None]:
99            content.append(name)
100            content.push_indent()
101            yield
102            content.pop_indent()
103
104        if wrap:
105            self.wrap(definition, context=_definition_item_context)
106        else:
107            self.add(definition, context=_definition_item_context)
108
109    @contextmanager
110    def definition_item(self, name: GenericContent) -> Iterator[None]:
111        """ Opens a definition item context. """
112        self.wrap(name)
113        self.push_indent()
114        yield
115        self.pop_indent()
116
117    def open_directive(self,
118                       name: str,
119                       value: Optional[str] = None,
120                       options: Optional[List[str]] = None) -> None:
121        """ Opens a directive. """
122        value = " " + value if value else ""
123        self.add(f".. {name.strip()}::{value}")
124        self.push_indent()
125        self.add(options)
126        self.gap = True
127
128    def close_directive(self) -> None:
129        """ Closes a directive. """
130        self.pop_indent()
131
132    @contextmanager
133    def directive(self,
134                  name: str,
135                  value: Optional[str] = None,
136                  options: Optional[List[str]] = None):
137        """ Opens a directive context. """
138        self.open_directive(name, value, options)
139        yield
140        self.close_directive()
141
142    def open_section(self,
143                     name: str,
144                     label_prefix: Optional[str] = None) -> str:
145        """ Opens a section. """
146        label = self.add_header_with_label(name, self._section_level,
147                                           label_prefix)
148        self._section_level += 1
149        return label
150
151    def close_section(self) -> None:
152        """ Closes a section. """
153        self._section_level -= 1
154
155    @contextmanager
156    def section(self,
157                name: str,
158                label_prefix: Optional[str] = None) -> Iterator[str]:
159        """ Opens a section context. """
160        yield self.open_section(name, label_prefix)
161        self.close_section()
162
163    def add_list_item(self, content: GenericContent) -> None:
164        """ Adds a list item. """
165        self.wrap(content, initial_indent="* ", subsequent_indent="  ")
166
167    def add_list(self,
168                 items: GenericContentIterable,
169                 prologue: Optional[GenericContent] = None,
170                 epilogue: Optional[GenericContent] = None,
171                 add_blank_line: bool = False) -> None:
172        """ Adds a list with introduction. """
173        if items:
174            self.wrap(prologue)
175            for item in items:
176                self.add_list_item(item)
177            if add_blank_line:
178                self.add_blank_line()
179            self.wrap(epilogue)
180
181    def open_list_item(self, content: GenericContent) -> None:
182        """ Opens a list item. """
183        self.add(["* "])
184        self.push_indent("  ")
185        self.gap = True
186        self.paste(content)
187
188    def close_list_item(self) -> None:
189        """ Closes a list item. """
190        self.pop_indent()
191        self.gap = True
192
193    @contextmanager
194    def list_item(self, content: GenericContent) -> Iterator[None]:
195        """ Opens a list item context. """
196        self.open_list_item(content)
197        yield
198        self.close_list_item()
199
200    def add_licence_and_copyrights(self) -> None:
201        """
202        Adds a licence and copyright block according to the registered licenses
203        and copyrights.
204        """
205        statements = self._copyrights.get_statements()
206        if statements:
207            self.prepend("")
208            self.prepend([f".. {stm}" for stm in statements])
209        self.prepend([f".. SPDX-License-Identifier: {self._license}", ""])
210
211
212def _get_ref_term(ctx: ItemGetValueContext) -> Any:
213    return f":term:`{ctx.value[ctx.key]}`"
214
215
216def _get_ref_term_plural(ctx: ItemGetValueContext) -> Any:
217    try:
218        return f":term:`{ctx.value[ctx.key]} <{ctx.value['term']}>`"
219    except KeyError:
220        return f":term:`{ctx.value['term']}s <{ctx.value['term']}>`"
221
222
223def _get_appl_config_option(ctx: ItemGetValueContext) -> Any:
224    return f":ref:`{ctx.value[ctx.key]}`"
225
226
227class SphinxMapper(ItemMapper):
228    """ Sphinx item mapper. """
229    def __init__(self, item: Item):
230        super().__init__(item)
231        self.add_get_value("glossary/term:/term", _get_ref_term)
232        self.add_get_value("glossary/term:/plural", _get_ref_term_plural)
233        self.add_get_value("interface/appl-config-option/feature-enable:/name",
234                           _get_appl_config_option)
235        self.add_get_value("interface/appl-config-option/feature:/name",
236                           _get_appl_config_option)
237        self.add_get_value("interface/appl-config-option/initializer:/name",
238                           _get_appl_config_option)
239        self.add_get_value("interface/appl-config-option/integer:/name",
240                           _get_appl_config_option)
Note: See TracBrowser for help on using the repository browser.