Create directive which wraps other directives inside

77 Views Asked by At

We are trying to create a directive which wraps other directives inside. The following example will show an example:

Directives
==========

.. service_card_wrapper::

    .. service_card::

    .. service_card::

We did not find a proper way to render the content of the "service_card" to the "service_card_wrapper". We need at least HTML content as result something like:

<div class="service-card-wrapper">
  <div class="service-card">
    Content 1
  </div>
  <div class="service-card">
    Content 2
  </div>
</div>

This is our first code-example with some Bootstrap Content:

from docutils import nodes

from docutils.parsers.rst import Directive
from docutils.parsers.rst import directives
from sphinx.util import logging


LOG = logging.getLogger(__name__)


class service_card(nodes.General, nodes.Element):
    pass


class ServiceCard(Directive):
    node_class = service_card
    option_spec = {
        # 'service_type': directives.unchanged_required,
    }

    has_content = False

    def run(self):
        node = self.node_class()
        # node['service_type'] = self.options.get('service_type')
        return [node]

def service_card_html(self, node):
    # This method renders containers per each service of the category with all
    # links as individual list items
    data = '''
        <div class="card">
        <h5 class="card-header">Featured</h5>
        <div class="card-body">
        <h5 class="card-title">Special title treatment</h5>
        <p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
        <a href="#" class="btn btn-primary">Go somewhere</a>
        </div>
        </div>'''
    self.body.append(data)
    raise nodes.SkipNode

def setup(app):
    app.add_node(service_card,
                 html=(service_card_html, None))
    app.add_directive("service_card", ServiceCard)

    return {
        'version': '0.1',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }

But we do not find a proper solution for the service card wrapper to get this content inside. Can anyone help?

We tried to create a service_card_wrapper but we never came to a result.

1

There are 1 best solutions below

0
edKotinsky On

You're looking for state.nested_parse. You can find the source code in the docutils parsers/states/rst/states.py. Also see the usage here and here.

This is the signature and the doc string from the docutils source code:

def nested_parse(self, block, input_offset, node, match_titles=False,
                 state_machine_class=None, state_machine_kwargs=None):
    """
    Create a new StateMachine rooted at `node` and run it over the input
    `block`.
    """

I've modified your code. Now the following RST text

Directives
==========

.. service-card-wrapper::

    .. service-card::

    .. service-card::

Gives the following HTML code, which is close enough, I think:

<div class="service-card-wrapper docutils container">
  <div class="service-card">
    <p>Service card content</p>
  </div>
  <div class="service-card">
    <p>Service card content</p>
  </div>
</div>

Code:

import docutils.nodes as nodes

from docutils.parsers.rst import Directive

from sphinx.util import logging

LOG = logging.getLogger(__name__)


class service_card(nodes.General, nodes.Element):
    pass


class ServiceCard(Directive):
    node_class = service_card
    option_spec = {
        # 'service_type': directives.unchanged_required,
    }

    has_content = False

    def run(self):
        node = self.node_class()
        # node['service_type'] = self.options.get('service_type')
        return [node]


class ServiceCardWrapper(Directive):
    has_content = True

    def run(self):
        div = nodes.container('', classes=['service-card-wrapper'], type='div')
        self.state.nested_parse(self.content, self.content_offset, div)
        return [div]


def visit_service_card(self, node):
    # This method renders containers per each service of the category with all
    # links as individual list items
    data = '''
        <div class="service-card">
        <p>Service card content</p>
        </div>'''
    self.body.append(data)


def service_card_depart(self, node):
    pass


def setup(app):
    app.add_node(service_card,
                 html=(visit_service_card, service_card_depart))
    app.add_directive("service-card", ServiceCard)
    app.add_directive("service-card-wrapper", ServiceCardWrapper)

    return {
        'version': '0.1',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }