From 135e77f93d0ab1f99fd0e915f953acb2653177fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Pit-Claudel?= <clement.pitclaudel@live.com>
Date: Thu, 31 Mar 2022 22:20:55 -0700
Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20Spurious=20newline=20in?=
 =?UTF-8?q?=20generated=20literal=5Fblock=20nodes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #407.
---
 myst_parser/docutils_renderer.py | 33 ++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)

diff --git a/myst_parser/docutils_renderer.py b/myst_parser/docutils_renderer.py
index 4494b549..4275ef02 100644
--- a/myst_parser/docutils_renderer.py
+++ b/myst_parser/docutils_renderer.py
@@ -78,6 +78,12 @@ def token_line(token: SyntaxTreeNode, default: Optional[int] = None) -> int:
     return token.map[0]  # type: ignore[index]
 
 
+def create_literal_block(rawsource='', text='', *children, **attributes):
+    if text.endswith("\n"):
+        text = text[:-1]
+    return nodes.literal_block(rawsource, text, *children, **kwargs)
+
+
 class DocutilsRenderer(RendererProtocol):
     """A markdown-it-py renderer to populate (in-place) a `docutils.document` AST.
 
@@ -510,15 +516,18 @@ def create_highlighted_code_block(
 
         Note, this function does not add the literal block to the document.
         """
+        rawsource = text
+        if text.endswith("\n"):
+            rawsource, text = text, text[:-1]
         if self.sphinx_env is not None:
-            node = node_cls(text, text, language=lexer_name or "none")
+            node = node_cls(rawsource, text, language=lexer_name or "none")
             if number_lines:
                 node["linenos"] = True
                 if lineno_start != 1:
                     node["highlight_args"] = {"linenostart": lineno_start}
         else:
             node = node_cls(
-                text, classes=["code"] + ([lexer_name] if lexer_name else [])
+                rawsource, classes=["code"] + ([lexer_name] if lexer_name else [])
             )
             try:
                 lex_tokens = Lexer(
@@ -780,7 +789,7 @@ def render_front_matter(self, token: SyntaxTreeNode) -> None:
                 msg_node = self.reporter.error(
                     "Front matter block:\n" + str(error), line=position
                 )
-                msg_node += nodes.literal_block(token.content, token.content)
+                msg_node += create_literal_block(token.content, token.content)
                 self.current_node.append(msg_node)
                 return
         else:
@@ -801,14 +810,14 @@ def render_front_matter(self, token: SyntaxTreeNode) -> None:
             msg_node = self.reporter.error(
                 "Front-matter 'substitutions' is not a dict", line=position
             )
-            msg_node += nodes.literal_block(token.content, token.content)
+            msg_node += create_literal_block(token.content, token.content)
             self.current_node.append(msg_node)
 
         if not isinstance(html_meta, dict):
             msg_node = self.reporter.error(
                 "Front-matter 'html_meta' is not a dict", line=position
             )
-            msg_node += nodes.literal_block(token.content, token.content)
+            msg_node += create_literal_block(token.content, token.content)
             self.current_node.append(msg_node)
 
         self.current_node.extend(
@@ -1108,7 +1117,7 @@ def render_dl(self, token: SyntaxTreeNode) -> None:
                                 "Found a definition in a definition list, "
                                 "with no preceding term"
                             ),
-                            # nodes.literal_block(content, content),
+                            # create_literal_block(content, content),
                             line=token_line(child),
                         )
                         self.current_node += [error]
@@ -1123,7 +1132,7 @@ def render_dl(self, token: SyntaxTreeNode) -> None:
                             "Expected a term/definition as a child of a definition list"
                             f", but found a: {child.type}"
                         ),
-                        # nodes.literal_block(content, content),
+                        # create_literal_block(content, content),
                         line=token_line(child),
                     )
                     self.current_node += [error_msg]
@@ -1143,7 +1152,7 @@ def render_field_list(self, token: SyntaxTreeNode) -> None:
                             "Expected a fieldlist_name as a child of a field_list"
                             f", but found a: {child.type}"
                         ),
-                        # nodes.literal_block(content, content),
+                        # create_literal_block(content, content),
                         line=token_line(child),
                     )
                     self.current_node += [error_msg]
@@ -1214,7 +1223,7 @@ def run_directive(
         if not directive_class:
             error = self.reporter.error(
                 'Unknown directive type "{}".\n'.format(name),
-                # nodes.literal_block(content, content),
+                # create_literal_block(content, content),
                 line=position,
             )
             return [error] + messages
@@ -1232,7 +1241,7 @@ def run_directive(
         except DirectiveParsingError as error:
             error = self.reporter.error(
                 "Directive '{}': {}".format(name, error),
-                nodes.literal_block(content, content),
+                create_literal_block(content, content),
                 line=position,
             )
             return [error]
@@ -1276,14 +1285,14 @@ def run_directive(
             msg_node = self.reporter.system_message(
                 error.level, error.msg, line=position
             )
-            msg_node += nodes.literal_block(content, content)
+            msg_node += create_literal_block(content, content)
             result = [msg_node]
         except MockingError as exc:
             error_msg = self.reporter.error(
                 "Directive '{}' cannot be mocked: {}: {}".format(
                     name, exc.__class__.__name__, exc
                 ),
-                nodes.literal_block(content, content),
+                create_literal_block(content, content),
                 line=position,
             )
             return [error_msg]