diff --git a/docs/layout_styles.rst b/docs/layout_styles.rst index d4d3d9bce..759f84fb5 100644 --- a/docs/layout_styles.rst +++ b/docs/layout_styles.rst @@ -357,6 +357,7 @@ Available layout functions are: * :func:`meta_links ` * :func:`meta_links_all ` * :func:`meta_id ` +* :func:`meta_rst ` * :func:`image ` * :func:`link ` * :func:`permalink ` @@ -372,6 +373,8 @@ Available layout functions are: .. autofunction:: sphinx_needs.layout.LayoutHandler.meta_links_all(prefix='', postfix='', exclude=None) +.. autofunction:: sphinx_needs.layout.LayoutHandler.meta_rst(prefix='', postfix='', exclude=None) + .. autofunction:: sphinx_needs.layout.LayoutHandler.image(url, height=None, width=None, align=None, no_link=False, prefix="", is_external=False, img_class="") .. autofunction:: sphinx_needs.layout.LayoutHandler.link(url, text=None, image_url=None, image_height=None, image_width=None, prefix="", is_dynamic=False) diff --git a/sphinx_needs/layout.py b/sphinx_needs/layout.py index d3652c2d5..efdab4aeb 100644 --- a/sphinx_needs/layout.py +++ b/sphinx_needs/layout.py @@ -79,11 +79,12 @@ def build_need_repr( @lru_cache(1) -def _generate_inline_parser() -> tuple[Values, Inliner]: +def _generate_parsers() -> tuple[Values, Inliner, Parser]: doc_settings = OptionParser(components=(Parser,)).get_default_values() inline_parser = Inliner() inline_parser.init_customizations(doc_settings) # type: ignore - return doc_settings, inline_parser + multiline_parser = Parser() + return doc_settings, inline_parser, multiline_parser class LayoutHandler: @@ -262,7 +263,9 @@ def __init__( } # Dummy Document setup - self.doc_settings, self.inline_parser = _generate_inline_parser() + self.doc_settings, self.inline_parser, self.multiline_parser = ( + _generate_parsers() + ) self.dummy_doc = new_document("dummy", self.doc_settings) self.doc_language = languages.get_language( self.dummy_doc.settings.language_code @@ -285,6 +288,7 @@ def __init__( "meta_links": self.meta_links, "meta_links_all": self.meta_links_all, # type: ignore[dict-item] "meta_id": self.meta_id, + "meta_rst": self.meta_rst, # type: ignore[dict-item] "image": self.image, # type: ignore[dict-item] "link": self.link, "collapse_button": self.collapse_button, @@ -339,14 +343,14 @@ def get_section(self, section: str) -> nodes.line_block | list[nodes.Element]: # line_block_node = nodes.line_block() line_node = nodes.line() - line_parsed = self._parse(line) + line_parsed = self._parse_inline(line) line_ready = self._func_replace(line_parsed) line_node += line_ready lines_container.append(line_node) return lines_container - def _parse(self, line: str) -> list[nodes.Node]: + def _parse_inline(self, line: str) -> list[nodes.Node]: """ Parses a single line/string for inline rst statements, like strong, emphasis, literal, ... @@ -485,7 +489,7 @@ def meta( """ data_container = nodes.inline(classes=["needs_" + name]) if prefix: - prefix_node = self._parse(prefix) + prefix_node = self._parse_inline(prefix) label_node = nodes.inline(classes=["needs_label"]) label_node += prefix_node data_container.append(label_node) @@ -707,7 +711,7 @@ def meta_links_all( outgoing_label = ( prefix + "{}:".format(link_type["outgoing"]) + postfix + " " ) - outgoing_line += self._parse(outgoing_label) + outgoing_line += self._parse_inline(outgoing_label) outgoing_line += self.meta_links(link_type["option"], incoming=False) data_container.append(outgoing_line) @@ -717,12 +721,61 @@ def meta_links_all( incoming_label = ( prefix + "{}:".format(link_type["incoming"]) + postfix + " " ) - incoming_line += self._parse(incoming_label) + incoming_line += self._parse_inline(incoming_label) incoming_line += self.meta_links(link_type["option"], incoming=True) data_container.append(incoming_line) return data_container + def meta_rst( + self, name: str, prefix: str | None = None, show_empty: bool = False + ) -> nodes.paragraph | list[nodes.Element]: + """ + Returns the specific metadata of a need inside docutils nodes, parsed as multiline rst. + Usage:: + + <> + + .. note:: + + You must escape all rst_content in your function strings! E.g. to get `**` one must use `\\\\*\\\\*`. + + :param name: name of the need item + :param prefix: string as rst-code, will be added infront of the value output + :param show_empty: If false and requested need-value is None or '', no output is returned. Default: false + :return: docutils node + """ + data_container = nodes.paragraph(classes=["needs_" + name]) + if prefix: + prefix_node = self._parse_inline(prefix) + label_node = nodes.inline(classes=["needs_label"]) + label_node += prefix_node + data_container.append(label_node) + try: + data = self.need[name] # type: ignore[literal-required] + except KeyError: + data = "" + + if data is None and not show_empty: + return [] + elif data is None and show_empty: + data = "" + + if isinstance(data, str): + if len(data) == 0 and not show_empty: + return [] + + dummy_doc = self.dummy_doc.deepcopy() + self.multiline_parser.parse(data, dummy_doc) + data_node = dummy_doc.children + + data_container += data_node + + else: + data_container.append(nodes.Text(data)) + + return data_container + def image( self, url: str, @@ -776,7 +829,7 @@ def image( data_container = nodes.inline() if prefix: - prefix_node = self._parse(prefix) + prefix_node = self._parse_inline(prefix) label_node = nodes.inline(classes=["needs_label"]) label_node += prefix_node data_container.append(label_node) @@ -901,7 +954,7 @@ def link( """ data_container = nodes.inline() if prefix: - prefix_node = self._parse(prefix) + prefix_node = self._parse_inline(prefix) label_node = nodes.inline(classes=["needs_label"]) label_node += prefix_node data_container.append(label_node)