Repository: executablebooks/MyST-Parser
Pull request #900 (2024-03-31 16:43:33)

✨ Allow for use of the `line-block` directive
The `line-block` directive constructs a block where all line breaks (and also initial indentations) are respected.

This was not previously supported, due to a missing State method

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/900?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) Report
Attention: Patch coverage is `81.81818%` with `4 lines` in your changes are missing coverage. Please review.
> Project coverage is 90.07%. Comparing base [(`dfaf6d7`)](https://app.codecov.io/gh/executablebooks/MyST-Parser/commit/dfaf6d7abfdd988c74a1d6e1bbdc637e9f5b5bcb?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) to head [(`70f44f5`)](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/900?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks).

| [Files](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/900?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) | Patch % | Lines |
|---|---|---|
| [myst\_parser/mocking.py](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/900?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks#diff-bXlzdF9wYXJzZXIvbW9ja2luZy5weQ==) | 81.81% | [4 Missing :warning: ](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/900?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #900      +/-   ##
==========================================
- Coverage   90.12%   90.07%   -0.06%     
==========================================
  Files          24       24              
  Lines        3383     3405      +22     
==========================================
+ Hits         3049     3067      +18     
- Misses        334      338       +4     
```

| [Flag](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/900/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) | Coverage Δ | |
|---|---|---|
| [pytests](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/900/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks) | `90.07% <81.81%> (-0.06%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks#carryforward-flags-in-the-pull-request-comment) to find out more.


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/executablebooks/MyST-Parser/pull/900?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=executablebooks).


Patch:
diff --git a/docs/syntax/typography.md b/docs/syntax/typography.md
index c6dbf8d..66eb28e 100644
--- a/docs/syntax/typography.md
+++ b/docs/syntax/typography.md
@@ -87,11 +87,24 @@ A paragraph with a span of [text with attributes]{.bg-warning}
 To put a line break, without a paragraph, use a `\` followed by a new line. This corresponds to a `<br>` in HTML and `\\` in LaTeX.
 
 :::{myst-example}
-Fleas \
+**Fleas** \
 Adam \
 Had 'em.
 :::
 
+```{versionadded} 3.0
+```
+
+Alternatively, you can use the `line-block` directive, which constructs a block where all line breaks (and also initial indentations) are respected:
+
+::::{myst-example}
+:::{line-block}
+**Fleas**
+  Adam
+  Had 'em.
+:::
+::::
+
 ## Bullet points and numbered lists
 
 You can use bullet points and numbered lists as you would in standard Markdown.
diff --git a/myst_parser/mocking.py b/myst_parser/mocking.py
index f37d9ef..3a73990 100644
--- a/myst_parser/mocking.py
+++ b/myst_parser/mocking.py
@@ -263,6 +263,35 @@ class MockState:
     def build_table_row(self, rowdata, tableline):
         return Body.build_table_row(self, rowdata, tableline)
 
+    def nest_line_block_lines(self, block: nodes.line_block):
+        """Modify the line block element in-place, to nest line block segments.
+
+        Line nodes are placed into child line block containers, based on their indentation.
+        """
+        for index in range(1, len(block)):
+            if getattr(block[index], "indent", None) is None:
+                block[index].indent = block[index - 1].indent
+        self._nest_line_block_segment(block)
+
+    def _nest_line_block_segment(self, block: nodes.line_block):
+        indents = [item.indent for item in block]
+        least = min(indents)
+        new_items = []
+        new_block = nodes.line_block()
+        for item in block:
+            if item.indent > least:
+                new_block.append(item)
+            else:
+                if len(new_block):
+                    self._nest_line_block_segment(new_block)
+                    new_items.append(new_block)
+                    new_block = nodes.line_block()
+                new_items.append(item)
+        if len(new_block):
+            self._nest_line_block_segment(new_block)
+            new_items.append(new_block)
+        block[:] = new_items
+
     def __getattr__(self, name: str):
         """This method is only be called if the attribute requested has not
         been defined. Defined attributes will not be overridden.


Test patch:
diff --git a/tests/test_renderers/fixtures/docutil_directives.md b/tests/test_renderers/fixtures/docutil_directives.md
index e15bf2e..e2a7df1 100644
--- a/tests/test_renderers/fixtures/docutil_directives.md
+++ b/tests/test_renderers/fixtures/docutil_directives.md
@@ -1,4 +1,3 @@
---------------------------------
 [attention] (`docutils.parsers.rst.directives.admonitions.Attention`):
 .
 ```{attention}
@@ -12,7 +11,6 @@ a
             a
 .
 
---------------------------------
 [caution] (`docutils.parsers.rst.directives.admonitions.Caution`):
 .
 ```{caution}
@@ -26,7 +24,6 @@ a
             a
 .
 
---------------------------------
 [danger] (`docutils.parsers.rst.directives.admonitions.Danger`):
 .
 ```{danger}
@@ -40,7 +37,6 @@ a
             a
 .
 
---------------------------------
 [error] (`docutils.parsers.rst.directives.admonitions.Error`):
 .
 ```{error}
@@ -54,7 +50,6 @@ a
             a
 .
 
---------------------------------
 [important] (`docutils.parsers.rst.directives.admonitions.Important`):
 .
 ```{important}
@@ -68,7 +63,6 @@ a
             a
 .
 
---------------------------------
 [note] (`docutils.parsers.rst.directives.admonitions.Note`):
 .
 ```{note}
@@ -82,7 +76,6 @@ a
             a
 .
 
---------------------------------
 [tip] (`docutils.parsers.rst.directives.admonitions.Tip`):
 .
 ```{tip}
@@ -96,7 +89,6 @@ a
             a
 .
 
---------------------------------
 [hint] (`docutils.parsers.rst.directives.admonitions.Hint`):
 .
 ```{hint}
@@ -110,7 +102,6 @@ a
             a
 .
 
---------------------------------
 [warning] (`docutils.parsers.rst.directives.admonitions.Warning`):
 .
 ```{warning}
@@ -124,7 +115,6 @@ a
             a
 .
 
---------------------------------
 [admonition] (`docutils.parsers.rst.directives.admonitions.Admonition`):
 .
 ```{admonition} myclass
@@ -140,7 +130,6 @@ a
             a
 .
 
---------------------------------
 [sidebar] (`docutils.parsers.rst.directives.body.Sidebar`):
 .
 ```{sidebar} sidebar title
@@ -156,7 +145,6 @@ a
             a
 .
 
---------------------------------
 [topic] (`docutils.parsers.rst.directives.body.Topic`):
 .
 ```{topic} Topic Title
@@ -172,18 +160,25 @@ a
             a
 .
 
---------------------------------
-[line-block] (`docutils.parsers.rst.directives.body.LineBlock`) SKIP: MockingError: MockState has not yet implemented attribute 'nest_line_block_lines'
+[line-block] (`docutils.parsers.rst.directives.body.LineBlock`)
 .
 ```{line-block}
-
-
+line 1
+line 2
+  line 3
 ```
 .
 <document source="notset">
+    <line_block>
+        <line>
+            line 1
+        <line>
+            line 2
+        <line_block>
+            <line>
+                line 3
 .
 
---------------------------------
 [parsed-literal] (`docutils.parsers.rst.directives.body.ParsedLiteral`):
 .
 ```{parsed-literal}
@@ -196,7 +191,6 @@ a
         a
 .
 
---------------------------------
 [rubric] (`docutils.parsers.rst.directives.body.Rubric`):
 .
 ```{rubric} Rubric Title
@@ -207,7 +201,6 @@ a
         Rubric Title
 .
 
---------------------------------
 [epigraph] (`docutils.parsers.rst.directives.body.Epigraph`):
 .
 ```{epigraph}
@@ -225,7 +218,6 @@ a
             attribution
 .
 
---------------------------------
 [highlights] (`docutils.parsers.rst.directives.body.Highlights`):
 .
 ```{highlights}
@@ -243,7 +235,6 @@ a
             attribution
 .
 
---------------------------------
 [pull-quote] (`docutils.parsers.rst.directives.body.PullQuote`):
 .
 ```{pull-quote}
@@ -261,7 +252,6 @@ a
             attribution
 .
 
---------------------------------
 [compound] (`docutils.parsers.rst.directives.body.Compound`):
 .
 ```{compound}
@@ -275,7 +265,6 @@ a
             a
 .
 
---------------------------------
 [container] (`docutils.parsers.rst.directives.body.Container`):
 .
 ```{container}
@@ -289,7 +278,6 @@ a
             a
 .
 
---------------------------------
 [image] (`docutils.parsers.rst.directives.images.Image`):
 .
 ```{image} path/to/image
@@ -301,7 +289,6 @@ a
     <image alt="abc" ids="name" names="name" uri="path/to/image">
 .
 
---------------------------------
 [raw] (`docutils.parsers.rst.directives.misc.Raw`):
 .
 ```{raw} raw
@@ -314,7 +301,6 @@ a
         a
 .
 
---------------------------------
 [class] (`docutils.parsers.rst.directives.misc.Class`):
 .
 ```{class} myclass
@@ -327,7 +313,6 @@ a
         a
 .
 
---------------------------------
 [role] (`docutils.parsers.rst.directives.misc.Role`) + raw (`docutils.parsers.rst.roles.raw_role`):
 .
 ```{role} raw-latex(raw)
@@ -342,7 +327,6 @@ a
             \tag{content}
 .
 
---------------------------------
 [title] (`docutils.parsers.rst.directives.misc.Title`):
 .
 ```{title} title
@@ -351,7 +335,6 @@ a
 <document source="notset" title="title">
 .
 
---------------------------------
 [restructuredtext-test-directive] (`docutils.parsers.rst.directives.misc.TestDirective`):
 .
 ```{restructuredtext-test-directive}
@@ -363,7 +346,6 @@ a
             Directive processed. Type="restructuredtext-test-directive", arguments=[], options={}, content: None
 .
 
---------------------------------
 [contents] (`docutils.parsers.rst.directives.parts.Contents`):
 .
 ```{contents} Contents
@@ -379,7 +361,6 @@ a
                  .details:
 .
 
---------------------------------
 [sectnum] (`docutils.parsers.rst.directives.parts.Sectnum`):
 .
 ```{sectnum}
@@ -392,7 +373,6 @@ a
              .details:
 .
 
---------------------------------
 [header] (`docutils.parsers.rst.directives.parts.Header`):
 .
 ```{header}
@@ -407,7 +387,6 @@ a
                 a
 .
 
---------------------------------
 [footer] (`docutils.parsers.rst.directives.parts.Footer`):
 .
 ```{footer}
@@ -422,7 +401,6 @@ a
                 a
 .
 
---------------------------------
 [target-notes] (`docutils.parsers.rst.directives.references.TargetNotes`):
 .
 ```{target-notes}