Skip to content

Instantly share code, notes, and snippets.

@brabect1
Last active June 29, 2024 11:48
Show Gist options
  • Select an option

  • Save brabect1/f8260648e27bd742b6227f8af6c07c35 to your computer and use it in GitHub Desktop.

Select an option

Save brabect1/f8260648e27bd742b6227f8af6c07c35 to your computer and use it in GitHub Desktop.

Revisions

  1. brabect1 revised this gist Jun 29, 2024. 1 changed file with 114 additions and 0 deletions.
    114 changes: 114 additions & 0 deletions xper_custom_directive.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,114 @@
    #! /usr/bin/env python

    # Copyright 2024 Tomas Brabec
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    try:
    import locale
    locale.setlocale(locale.LC_ALL, '')
    except:
    pass

    import docutils.frontend
    import docutils.utils
    import docutils.parsers
    import docutils.parsers.rst
    import docutils.parsers.rst.directives

    # this is merely to specifically identify elements derived
    # from the `my_extension` directive
    class mydirective(docutils.nodes.Element): pass

    class MyExtensionDirective(docutils.parsers.rst.Directive):
    required_arguments = 1;
    optional_arguments = 0;
    final_argument_whitespace = True;
    has_content = True;
    option_spec = {'my_attr': docutils.parsers.rst.directives.unchanged,
    }

    def run(self):
    self.assert_has_content()
    text = '\n'.join(self.content)
    n = mydirective(text, **self.options)
    self.add_name(n)

    # this will turn the directive argument into a title node
    # (as we generally allow the title to contain inline markup,
    # we use the inline parser to get (`textnodes`) list of inline
    # elements/nodes)
    title_text = self.arguments[0]
    textnodes, messages = self.state.inline_text(title_text,
    self.lineno)
    # the following would end up calling TextElement(rawsource,text,*children) constructor
    title = docutils.nodes.title(title_text, '', *textnodes)
    title.source, title.line = (
    self.state_machine.get_source_and_line(self.lineno))

    # add a new sub-node and any system messages from the inline parsing
    n += title
    n += messages

    # This will parse the directive content
    self.state.nested_parse(self.content, self.content_offset,
    n)
    return [n]

    # This overrides what directive class will be used to process the `my_extension` directive
    docutils.parsers.rst.directives._directives['my_extension'] = MyExtensionDirective;

    settings = docutils.frontend.OptionParser().get_default_values()

    parser_class = docutils.parsers.get_parser_class('restructuredtext')
    parser = parser_class()

    option_parser = docutils.frontend.OptionParser(
    components=(parser,),
    read_config_files=True,
    description='')

    settings = option_parser.parse_args()


    document = docutils.utils.new_document('', settings)

    text = '''
    My title
    ========
    1st paragraph
    Subtitle
    --------
    2nd paragraph
    .. my_extension:: This is my extension
    :my_attr: 1 2 3
    4 5 6
    1st extension paragraph
    #. 1st numbered item
    #. 2nd numbered item
    3rd paragraph
    - a
    - b
    '''

    parser.parse(text, document)

    print(document.pformat())
  2. brabect1 revised this gist Jun 24, 2024. 1 changed file with 26 additions and 19 deletions.
    45 changes: 26 additions & 19 deletions customizing_rst_syntax.rst
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,29 @@
    Custom Directives in reStructuredText (rst)
    ===========================================
    Customizing reStructuredText (rst) for Domain-specific Syntax
    =============================================================

    - document tree

    - created by a ``Parser`` component (i.e. from reStructuredText input)
    - manually constructed

    - ``docutils`` components

    - ``Reader``: Intended to obtain input and pass it to a ``Parser``.
    - ``Parser``: Generates a (docutils) document tree from a ``Reader`` input. docutils contains only a reStructuredText parser but potentially others may exists (e.g. markdown, html, etc.).
    - ``Writer``: Turns a document tree into an output (e.g. html, LaTeX).

    - other "pieces"

    - transforms: Called by a ``Parser`` for on-the-fly document tree manipulation.
    - visitors: Operate on an existing document tree (i.e. after parsing). Can collect various information and/or manipulate the document tree.
    - roles: ??? https://docutils.sourceforge.io/docs/ref/rst/roles.html (see example that creates a custom ``date`` role as alternative to a ``date::`` directive https://docutils.sourceforge.io/docs/ref/rst/directives.html#date)
    - directives: ???

    Custom Roles
    ------------

    Custom Directives
    -----------------

    https://docutils.sourceforge.io/docs/ref/rst/directives.html

    @@ -33,20 +57,3 @@ https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#directives

    howto: https://docutils.sourceforge.io/docs/howto/rst-directives.html

    - document tree

    - created by a ``Parser`` component (i.e. from reStructuredText input)
    - manually constructed

    - ``docutils`` components

    - ``Reader``: Intended to obtain input and pass it to a ``Parser``.
    - ``Parser``: Generates a (docutils) document tree from a ``Reader`` input. docutils contains only a reStructuredText parser but potentially others may exists (e.g. markdown, html, etc.).
    - ``Writer``: Turns a document tree into an output (e.g. html, LaTeX).

    - other "pieces"

    - transforms: Called by a ``Parser`` for on-the-fly document tree manipulation.
    - visitors: Operate on an existing document tree (i.e. after parsing). Can collect various information and/or manipulate the document tree.
    - roles: ??? https://docutils.sourceforge.io/docs/ref/rst/roles.html (see example that creates a custom ``date`` role as alternative to a ``date::`` directive https://docutils.sourceforge.io/docs/ref/rst/directives.html#date)
    - directives: ???
  3. brabect1 revised this gist Jun 20, 2024. 1 changed file with 80 additions and 0 deletions.
    80 changes: 80 additions & 0 deletions xper_manipulate_doctree.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,80 @@
    #! /usr/bin/env python

    # Copyright 2024 Tomas Brabec
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    try:
    import locale
    locale.setlocale(locale.LC_ALL, '')
    except:
    pass

    import docutils.frontend
    import docutils.utils
    import docutils.parsers

    def is_note(node):
    return node is not None and isinstance(node, docutils.nodes.note)

    def my_condition(node):
    return node is not None and isinstance(node, docutils.nodes.list_item) and node.astext() == 'a'

    settings = docutils.frontend.OptionParser().get_default_values()

    parser_class = docutils.parsers.get_parser_class('restructuredtext')
    parser = parser_class()

    option_parser = docutils.frontend.OptionParser(
    components=(parser,),
    read_config_files=True,
    description='')

    settings = option_parser.parse_args()


    document = docutils.utils.new_document('', settings)

    text = '''
    My title
    ========
    .. note:: 1st paragraph
    2nd paragraph
    3rd paragraph
    .. note:: 4th paragraph
    - a
    - b
    '''

    parser.parse(text, document)

    print(document.pformat())
    print(20 * '=')

    # in-place replacement of nodes
    for node in document.traverse(condition=is_note):
    parent = node.parent;
    childs = node.children;
    parent.replace(node,childs)
    # note: the above is equivalent to ``node.replace_self(node.children)``

    # deletion of nodes
    for node in document.traverse(condition=my_condition):
    node.parent.remove(node)

    print(document.pformat())
  4. brabect1 revised this gist Jun 20, 2024. 2 changed files with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion xper_title_visitor.py
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,4 @@
    #! /usr/bin/env python
    from orca.scripts import self_voicing

    # Copyright 2024 Tomas Brabec
    #
  5. brabect1 revised this gist Jun 19, 2024. 1 changed file with 7 additions and 2 deletions.
    9 changes: 7 additions & 2 deletions custom_rst_directives.rst
    Original file line number Diff line number Diff line change
    @@ -34,14 +34,19 @@ https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#directives
    howto: https://docutils.sourceforge.io/docs/howto/rst-directives.html

    - document tree

    - created by a ``Parser`` component (i.e. from reStructuredText input)
    - manually constructed

    - ``docutils`` components

    - ``Reader``: Intended to obtain input and pass it to a ``Parser``.
    - ``Parser``: Generates a (docutils) document tree from a ``Reader`` input. docutils contains only a reStructuredText parser but potentially others may exists (e.g. markdown, html, etc.).
    - ``Writer``: Turns a document tree into an output (e.g. html, LaTeX).

    - other "pieces"

    - transforms: Called by a ``Parser`` for on-the-fly document tree manipulation.
    - visitors: Operate on an existing document tree (i.e. after parsing). Can collect various information and/or manipulate the document tree.
    - roles: ???
    - directives: ???
    - roles: ??? https://docutils.sourceforge.io/docs/ref/rst/roles.html (see example that creates a custom ``date`` role as alternative to a ``date::`` directive https://docutils.sourceforge.io/docs/ref/rst/directives.html#date)
    - directives: ???
  6. brabect1 revised this gist Jun 19, 2024. 1 changed file with 50 additions and 0 deletions.
    50 changes: 50 additions & 0 deletions xper_publisher_rst2html.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,50 @@
    #! /usr/bin/env python

    # Copyright 2024 Tomas Brabec
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    try:
    import locale
    locale.setlocale(locale.LC_ALL, '')
    except:
    pass

    import docutils.io
    import docutils.core

    source = '''
    My title
    ========
    1st paragraph
    2nd paragraph
    - a
    - b
    '''

    # using string input and output
    pub = docutils.core.Publisher(source_class=docutils.io.StringInput, destination_class=docutils.io.StringOutput)

    reader_name='standalone'
    parser_name='restructuredtext'
    writer_name='html'

    pub.set_components(reader_name, parser_name, writer_name)
    settings = pub.get_settings(); # populates the publisher default settings (from individual components)
    pub.set_source(source=source, source_path=''); # assign input source
    output = pub.publish()

    print(output)
  7. brabect1 revised this gist Jun 19, 2024. 1 changed file with 66 additions and 0 deletions.
    66 changes: 66 additions & 0 deletions xper_date_role.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,66 @@
    #! /usr/bin/env python

    # Copyright 2024 Tomas Brabec
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    try:
    import locale
    locale.setlocale(locale.LC_ALL, '')
    except:
    pass

    import docutils.frontend
    import docutils.utils
    import docutils.parsers
    import docutils.nodes
    import docutils.parsers.rst.roles


    def date_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
    import datetime
    node = docutils.nodes.inline(rawtext, datetime.datetime.today().strftime('%Y-%m-%d'), **options)
    return [node], []

    docutils.parsers.rst.roles.register_local_role('date', date_role)

    settings = docutils.frontend.OptionParser().get_default_values()

    parser_class = docutils.parsers.get_parser_class('restructuredtext')
    parser = parser_class()

    option_parser = docutils.frontend.OptionParser(
    components=(parser,),
    read_config_files=True,
    description='')

    settings = option_parser.parse_args()


    document = docutils.utils.new_document('', settings)

    text = '''
    My title
    ========
    1st paragraph as of :date:`whatever` today
    2nd paragraph
    - a
    - b
    '''

    parser.parse(text, document)

    print(document.pformat())
  8. brabect1 revised this gist Jun 17, 2024. 1 changed file with 178 additions and 0 deletions.
    178 changes: 178 additions & 0 deletions xper_title_visitor.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,178 @@
    #! /usr/bin/env python
    from orca.scripts import self_voicing

    # Copyright 2024 Tomas Brabec
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    try:
    import locale
    locale.setlocale(locale.LC_ALL, '')
    except:
    pass

    import docutils.frontend
    import docutils.utils
    import docutils.parsers
    import docutils.nodes

    class TitleCollectVisitor(docutils.nodes.SparseNodeVisitor):

    def __init__(self, document):
    self.document = document
    self.titles = {};

    def visit_title(self,node):
    lvl = self.get_sec_level(node.parent);
    d = self.titles;
    if lvl >= 0:
    while lvl > 0:
    d = d[list(d)[-1]];
    lvl = lvl - 1;
    d[node.astext()] = {};

    def get_sec_level(self, node):
    """
    There are three cases to consider (all are due to `frontmatter`
    transform happening on the parsed input). First::
    Title
    =====
    Subtitle
    --------
    which yields::
    <document>
    <title>
    <subtitle>
    Second::
    Title
    =====
    Subtitle 1
    ----------
    Subtitle 2
    ----------
    which yields::
    <document>
    <title>
    <section>
    <title>
    <section>
    <title>
    And eventually third::
    Title 1
    =======
    Subtitle 1
    ----------
    Title 2
    =======
    which yields::
    <document>
    <section>
    <title>
    <section>
    <title>
    <section>
    <title>
    """
    if isinstance(node, docutils.nodes.document):
    # This will be called for the 1st (`title` and `subtitle`) and
    # 2nd case (`title` only)
    return 0
    elif isinstance(node, docutils.nodes.section):
    # This will be called for the 2nd (`subtitle` only) and 3rd case.
    # To distinguish the two, we need to see if the `document` node
    # contains a `title` child.
    i=0
    while node==None or not isinstance(node, docutils.nodes.document):
    node = node.parent
    i += 1
    if isinstance(node, docutils.nodes.document):
    hastitle = 0
    for c in node.children:
    if isinstance(c, docutils.nodes.title):
    hastitle = 1
    break
    if not hastitle:
    i -= 1
    return i
    else:
    return -1

    def print_titles(self, d=None, indent=''):
    if d is None:
    d = self.titles;

    for (k,v) in d.items():
    print(f"{indent}- {k}");
    self.print_titles(v, indent + ' ');

    settings = docutils.frontend.OptionParser().get_default_values()

    parser_class = docutils.parsers.get_parser_class('restructuredtext')
    parser = parser_class()

    option_parser = docutils.frontend.OptionParser(
    components=(parser,),
    read_config_files=True,
    description='')

    settings = option_parser.parse_args()


    document = docutils.utils.new_document('', settings)

    text = '''
    My title
    ========
    1st paragraph
    1st Subtitle
    ------------
    2nd paragraph
    1st SubSubtitle
    ...............
    2nd SubSubtitle
    ...............
    2nd Subtitle
    ------------
    - a
    - b
    '''

    parser.parse(text, document)

    visitor = TitleCollectVisitor(document)
    document.walkabout(visitor)

    visitor.print_titles()
  9. brabect1 revised this gist Jun 17, 2024. 1 changed file with 12 additions and 0 deletions.
    12 changes: 12 additions & 0 deletions custom_rst_directives.rst
    Original file line number Diff line number Diff line change
    @@ -33,3 +33,15 @@ https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#directives

    howto: https://docutils.sourceforge.io/docs/howto/rst-directives.html

    - document tree
    - created by a ``Parser`` component (i.e. from reStructuredText input)
    - manually constructed
    - ``docutils`` components
    - ``Reader``: Intended to obtain input and pass it to a ``Parser``.
    - ``Parser``: Generates a (docutils) document tree from a ``Reader`` input. docutils contains only a reStructuredText parser but potentially others may exists (e.g. markdown, html, etc.).
    - ``Writer``: Turns a document tree into an output (e.g. html, LaTeX).
    - other "pieces"
    - transforms: Called by a ``Parser`` for on-the-fly document tree manipulation.
    - visitors: Operate on an existing document tree (i.e. after parsing). Can collect various information and/or manipulate the document tree.
    - roles: ???
    - directives: ???
  10. brabect1 revised this gist Jun 16, 2024. 3 changed files with 101 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions custom_rst_directives.rst
    Original file line number Diff line number Diff line change
    @@ -30,3 +30,6 @@ Examples::
    * other directives

    https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#directives

    howto: https://docutils.sourceforge.io/docs/howto/rst-directives.html

    56 changes: 56 additions & 0 deletions xper_inline_text_construct.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    #! /usr/bin/env python

    # Copyright 2024 Tomas Brabec
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    try:
    import locale
    locale.setlocale(locale.LC_ALL, '')
    except:
    pass

    import docutils.frontend
    import docutils.utils
    import docutils.parsers

    settings = docutils.frontend.OptionParser().get_default_values()

    parser_class = docutils.parsers.get_parser_class('restructuredtext')
    parser = parser_class()

    option_parser = docutils.frontend.OptionParser(
    components=(parser,),
    read_config_files=True,
    description='')

    settings = option_parser.parse_args()


    document = docutils.utils.new_document('', settings)

    text = '''
    My title
    ========
    1st paragraph
    2nd paragraph
    - a
    - b
    '''

    parser.parse(text, document)

    print(document.pformat())
    42 changes: 42 additions & 0 deletions xper_manual_doc_construct.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,42 @@
    #! /usr/bin/env python

    # Copyright 2024 Tomas Brabec
    #
    # Licensed under the Apache License, Version 2.0 (the "License");
    # you may not use this file except in compliance with the License.
    # You may obtain a copy of the License at
    #
    # http://www.apache.org/licenses/LICENSE-2.0
    #
    # Unless required by applicable law or agreed to in writing, software
    # distributed under the License is distributed on an "AS IS" BASIS,
    # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    # See the License for the specific language governing permissions and
    # limitations under the License.

    try:
    import locale
    locale.setlocale(locale.LC_ALL, '')
    except:
    pass

    import docutils.utils

    document = docutils.utils.new_document('', None)

    document += docutils.nodes.title(text='My title');
    document += docutils.nodes.paragraph(text='1st paragraph');
    document += docutils.nodes.paragraph(text='2nd paragraph');

    node = docutils.nodes.bullet_list();
    node['bullet'] = '-';
    subnode = docutils.nodes.list_item('');
    subnode += docutils.nodes.paragraph(text='a');
    node += subnode;
    subnode = docutils.nodes.list_item('');
    subnode += docutils.nodes.paragraph(text='b');
    node += subnode;

    document += node;

    print(document.pformat())
  11. brabect1 revised this gist May 30, 2024. 1 changed file with 20 additions and 1 deletion.
    21 changes: 20 additions & 1 deletion custom_rst_directives.rst
    Original file line number Diff line number Diff line change
    @@ -10,4 +10,23 @@ Directives have the following syntax::
    +-------+ block |
    | |
    +-------------------------------+

    Examples::

    .. image:: /path/to/image.png
    .. figure:: /path/to/image.png
    :width: 600px
    .. note:: This is the note's 1st paragraph
    that spans multiple lines.
    This the note's 2nd paragraph.
    Notes are general blocks and can embed usual markup elements:
    * text elements with inline markup
    * list elements
    * other directives

    https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#directives
  12. brabect1 revised this gist May 30, 2024. 2 changed files with 0 additions and 0 deletions.
  13. brabect1 created this gist May 30, 2024.
    12 changes: 12 additions & 0 deletions custom_admonition_processing.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    import docutils
    from docutils.parsers.rst.directives.admonitions import BaseAdmonition

    # this is merely to specifically identify elements derived
    # from the `my_extension` directive
    class myadmonition(docutils.nodes.Admonition, docutils.nodes.Element): pass

    class MyExtensionAdmonition(BaseAdmonition):
    node_class = myadmonition;

    # This overrides what directive class will be used to process the `my_extension` directive
    directives._directives['my_extension'] = MyExtensionAdmonition;
    41 changes: 41 additions & 0 deletions custom_directive_processing.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,41 @@
    import docutils

    # this is merely to specifically identify elements derived
    # from the `my_extension` directive
    class mydirective(docutils.nodes.Element): pass

    class MyExtensionDirective(docutils.parsers.rst.Directive):
    required_arguments = 1;
    optional_arguments = 0;
    final_argument_whitespace = True;
    has_content = True;

    def run(self):
    self.assert_has_content()
    text = '\n'.join(self.content)
    n = mydirective(text, **self.options)
    self.add_name(n)

    # this will turn the directive argument into a title node
    # (as we generally allow the title to contain inline markup,
    # we use the inline parser to get (`textnodes`) list of inline
    # elements/nodes)
    title_text = self.arguments[0]
    textnodes, messages = self.state.inline_text(title_text,
    self.lineno)
    # the following would end up calling TextElement(rawsource,text,*children) constructor
    title = docutils.nodes.title(title_text, '', *textnodes)
    title.source, title.line = (
    self.state_machine.get_source_and_line(self.lineno))

    # add a new sub-node and any system messages from the inline parsing
    n += title
    n += messages

    # This will parse the directive content
    self.state.nested_parse(self.content, self.content_offset,
    n)
    return [n]

    # This overrides what directive class will be used to process the `my_extension` directive
    directives._directives['my_extension'] = MyExtensionDirective;
    13 changes: 13 additions & 0 deletions custom_rst_directives.rst
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    Custom Directives in reStructuredText (rst)
    ===========================================

    https://docutils.sourceforge.io/docs/ref/rst/directives.html

    Directives have the following syntax::

    +-------+-------------------------------+
    | ".. " | directive type "::" directive |
    +-------+ block |
    | |
    +-------------------------------+