Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 102 additions & 9 deletions docs/directives/list2need.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ The list-structure syntax is Sphinx-Needs specific, but borrowed from markdown.

Meta-data can be set via inline text. See :ref:`list2need_meta_data` for details.

Need-IDs get generated automatically (hash value), if not given.
IDs can be set by the prefix ``(ID)`` in the line. Example: ``(REQ-1)My first requirement``.
This mechanism is the same as the one used by :ref:`need_part`.
Need-IDs can be set by putting them in brackets in the line. Example: ``(REQ-1)My first requirement``.
If no ID is given, one gets generated from the title. See :ref:`list2need_ids` for both cases.

Options for the need-objects can be set by adding them like ``((status="open"))``.
For details please see :ref:`list2need_meta_data`.
Expand Down Expand Up @@ -67,8 +66,90 @@ Each line starting with a ``*`` will create a new need object.
To define a child-need, add **2 additional whitespaces** infront of ``*``.
This is called the indentation level and each level must have a need-type defined in the ``types`` option.

The indentation must be a multiple of 2 spaces, and one level is always exactly 2 spaces:
4 spaces mean level 2, not a second level 1.
An indentation that is not a multiple of 2 ends the build with an error.

Tabs cannot be used for the indentation.
Before the directive sees the line, a tab is expanded to the next tab stop (8 columns by default),
so the indentation it ends up producing depends on the column that tab happens to sit in.
A tab-indented line therefore usually ends the build,
and where the expansion does come out as a multiple of 2 it silently lands on a level that was never written.

A line starting **without** a ``*`` will be added to the prior one.
So it can be used to structure longer titles or content, and has no impact on the later representation.
Such a line must not start in the first column of the list; a line that does loses its first word.

.. _list2need_ids:

Need IDs
--------

An ID is recognised only as a bracketed group inside the line.
The group starts at the **first** ``(`` and, because it is matched greedily,
ends at the **last** ``)``.
It may contain any character except ``"``, ``'``, ``=`` and a line break,
which are excluded because the option syntax uses them.

Two consequences are worth knowing:

* A line that carries a second bracketed group, such as ``(REQ-1) The system (as defined) shall work``,
produces the ID ``REQ-1) The system (as defined``.
An ID of that shape is normally refused by :ref:`needs_id_regex`,
and the need is then not created at all.
* A line with no leading ID but with a parenthetical elsewhere, such as ``Some (draft) title``,
uses ``draft`` as the ID and removes it from the title.

So avoid brackets in a title that is also meant to carry an ID.

.. note::

Despite the similar look, this is **not** the mechanism used by :ref:`need_part`.
``need_part`` anchors its match to the start of the text and accepts only word characters and ``-``,
whereas ``list2need`` searches the whole line with a much wider character class.

Generated IDs
~~~~~~~~~~~~~

If a line contains no bracketed group, the ID gets generated from the title:

.. code-block:: text

<prefix of the need type> + SHA1(<title>) as uppercase hex, cut to needs_id_length

Only the title feeds the hash.
The content, the document and the position in the list take no part in it,
and the need type contributes its prefix but nothing to the hash.
So the ID stays the same when the list gets reordered or the document gets renamed,
it changes whenever the title changes,
and the same title used at two levels of one list gives two different IDs, one per prefix.
See :ref:`needs_id_length` for the length and :ref:`needs_types` for the prefix of each type.

.. warning::

Because the document is not part of the input,
two lines with the same title and the same need-type produce the **same ID**,
even in different documents.
The second need is then not created, and a warning reports the duplicated ID.
Give such needs an explicit ID.

Markdown (MyST)
---------------

``list2need`` currently works in reStructuredText documents only.
Written as a fenced ``{list2need}`` directive in a MyST Markdown document,
it reports an error and creates no needs.

As a workaround the directive can be written inside an ``eval-rst`` block:

.. code-block:: markdown

```{eval-rst}
.. list2need::
:types: req, spec

* (MD-REQ-1) A requirement written from Markdown
```

Options
-------
Expand Down Expand Up @@ -181,14 +262,19 @@ List with need-ids

* (LIST2NEED-001) Feature 1
* (LIST2NEED-002) Feature 2
* (FEATURE.3) Feature 3
* (LIST2NEED-003) Feature 3

.. list2need::
:types: feature, req, spec
:types: feature

* (LIST2NEED-001) Feature 1
* (LIST2NEED-002) Feature 2
* (FEATURE.3) Feature 3
* (LIST2NEED-003) Feature 3

Note that the ID must not contain the delimiter.
With the default delimiter ``.``, an ID such as ``(FEATURE.3)`` gets split before it is read,
which leaves ``(FEATURE`` as the title and generates an ID from it.
Use an ID without the delimiter, or set a different one via the ``delimiter`` option.

Nested lists
~~~~~~~~~~~~
Expand Down Expand Up @@ -311,9 +397,16 @@ Meta-data can be set directly in the related line via: ``((status="open"))``.
Or if the amount of option/values is getting too complex, in a second step
by using :ref:`needextend`.

The position of the option-string inside the line is not important.
Multiple options need to be separated by ``,``.
And instead of ``"`` also ``'`` can be used.
Only the **first** ``((...))`` region of a line is read, and the region is matched greedily:
it starts at the first ``((`` and ends at the last ``))``.
A line that carries two such regions therefore loses the text between them,
so keep all options of a need in one region.

Inside the region, options are written as ``name="value"`` pairs.
Instead of ``"`` also ``'`` can be used.
Text between two pairs is ignored, so the pairs may simply be separated by a space or by ``,``.

A value must be quoted. An unquoted value, as in ``((status=open))``, is silently ignored.

.. code-block:: rst

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ test = [
"pydantic~=2.12",
"pytest-xprocess~=1.0",
"pyyaml ~= 6.0",
"myst-parser>=4,<6", # host coverage for directives usable from Markdown
]
test-parallel = ["pytest-xdist"]
benchmark = [
Expand Down
Loading
Loading