← mined_oracle

rich_2247

failed WRONG_FIX UNSUBMITTED wrong_fix_unsubmitted(None) · None tool calls · 0 s · Textualize/rich

Task input

(not found in data/tasks.jsonl)

Tool calls (0)

#ToolArgumentsResult
No trace captured.

Patch

--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Change SVG export to create a simpler SVG
 - Fix render_lines crash when render height was negative https://github.com/Textualize/rich/pull/2246
 
+### Added
+
+- Add `padding` to Syntax constructor https://github.com/Textualize/rich/pull/2247
+
 ## [12.3.0] - 2022-04-26
 
 ### Added
--- a/rich/syntax.py
+++ b/rich/syntax.py
@@ -23,13 +23,14 @@ from pygments.token import (
 from pygments.util import ClassNotFound
 
 from rich.containers import Lines
+from rich.padding import Padding, PaddingDimensions
 
 from ._loop import loop_first
 from .color import Color, blend_rgb
 from .console import Console, ConsoleOptions, JustifyMethod, RenderResult
 from .jupyter import JupyterMixin
 from .measure import Measurement
-from .segment import Segment
+from .segment import Segment, Segments
 from .style import Style
 from .text import Text
 
@@ -100,6 +101,7 @@ ANSI_DARK: Dict[TokenType, Style] = {
 }
 
 RICH_SYNTAX_THEMES = {"ansi_light": ANSI_LIGHT, "ansi_dark": ANSI_DARK}
+NUMBERS_COLUMN_DEFAULT_PADDING = 2
 
 
 class SyntaxTheme(ABC):
@@ -209,6 +211,7 @@ class Syntax(JupyterMixin):
         word_wrap (bool, optional): Enable word wrapping.
         background_color (str, optional): Optional background color, or None to use theme color. Defaults to None.
         indent_guides (bool, optional): Show indent guides. Defaults to False.
+        padding (PaddingDimensions): Padding to apply around the syntax. Defaults to 0 (no padding).
     """
 
     _pygments_style_class: Type[PygmentsStyle]
@@ -242,6 +245,7 @@ class Syntax(JupyterMixin):
         word_wrap: bool = False,
         background_color: Optional[str] = None,
         indent_guides: bool = False,
+        padding: PaddingDimensions = 0,
     ) -> None:
         self.code = code
         self._lexer = lexer
@@ -258,6 +262,7 @@ class Syntax(JupyterMixin):
             Style(bgcolor=background_color) if background_color else Style()
         )
         self.indent_guides = indent_guides
+        self.padding = padding
 
         self._theme = self.get_theme(theme)
 
@@ -278,6 +283,7 @@ class Syntax(JupyterMixin):
         word_wrap: bool = False,
         background_color: Optional[str] = None,
         indent_guides: bool = False,
+        padding: PaddingDimensions = 0,
     ) -> "Syntax":
         """Construct a Syntax object from a file.
 
@@ -296,6 +302,7 @@ class Syntax(JupyterMixin):
             word_wrap (bool, optional): Enable word wrapping of code.
             background_color (str, optional): Optional background color, or None to use theme color. Defaults to None.
             indent_guides (bool, optional): Show indent guides. Defaults to False.
+            padding (PaddingDimensions): Padding to apply around the syntax. Defaults to 0 (no padding).
 
         Returns:
             [Syntax]: A Syntax object that may be printed to the console
@@ -320,6 +327,7 @@ class Syntax(JupyterMixin):
             word_wrap=word_wrap,
             background_color=background_color,
             indent_guides=indent_guides,
+            padding=padding,
         )
 
     @classmethod
@@ -498,7 +506,10 @@ class Syntax(JupyterMixin):
         """Get the number of characters used to render the numbers column."""
         column_width = 0
         if self.line_numbers:
-            column_width = len(str(self.start_line + self.code.count("\n"))) + 2
+            column_width = (
+                len(str(self.start_line + self.code.count("\n")))
+                + NUMBERS_COLUMN_DEFAULT_PADDING
+            )
         return column_width
 
     def _get_number_styles(self, console: Console) -> Tuple[Style, Style, Style]:
@@ -527,15 +538,31 @@ class Syntax(JupyterMixin):
     def __rich_measure__(
         self, console: "Console", options: "ConsoleOptions"
     ) -> "Measurement":
+        _, right, _, left = Padding.unpack(self.padding)
         if self.code_width is not None:
-            width = self.code_width + self._numbers_column_width
+            width = self.code_width + self._numbers_column_width + right + left
             return Measurement(self._numbers_column_width, width)
         return Measurement(self._numbers_column_width, options.max_width)
 
     def __rich_console__(
         self, console: Console, options: ConsoleOptions
     ) -> RenderResult:
+        segments = Segments(self._get_syntax(console, options))
+        if self.padding:
+            yield Padding(
+                segments, style=self._theme.get_background_style(), pad=self.padding
+            )
+        else:
+            yield segments
 
+    def _get_syntax(
+        self,
+        console: Console,
+        options: ConsoleOptions,
+    ) -> Iterable[Segment]:
+        """
+        Get the Segments for the Syntax object, excluding any vertical/horizontal padding
+        """
         transparent_background = self._get_base_style().transparent_background
         code_width = (
             (
@@ -553,12 +580,6 @@ class Syntax(JupyterMixin):
         code = code.expandtabs(self.tab_size)
         text = self.highlight(code, self.line_range)
 
-        (
-            background_style,
-            number_style,
-            highlight_number_style,
-        ) = self._get_number_styles(console)
-
         if not self.line_numbers and not self.word_wrap and not self.line_range:
             if not ends_on_nl:
                 text.remove_suffix("\n")
@@ -615,11 +636,16 @@ class Syntax(JupyterMixin):
 
         highlight_line = self.highlight_lines.__contains__
         _Segment = Segment
-        padding = _Segment(" " * numbers_column_width + " ", background_style)
         new_line = _Segment("\n")
 
         line_pointer = "> " if options.legacy_windows else "❱ "
 
+        (
+            background_style,
+            number_style,
+            highlight_number_style,
+        ) = self._get_number_styles(console)
+
         for line_no, line in enumerate(lines, self.start_line + line_offset):
             if self.word_wrap:
                 wrapped_lines = console.render_lines(
@@ -628,7 +654,6 @@ class Syntax(JupyterMixin):
                     style=background_style,
                     pad=not transparent_background,
                 )
-
             else:
                 segments = list(line.render(console, end=""))
                 if options.no_wrap:
@@ -642,7 +667,11 @@ class Syntax(JupyterMixin):
                             pad=not transparent_background,
                         )
                     ]
+
             if self.line_numbers:
+                wrapped_line_left_pad = _Segment(
+                    " " * numbers_column_width + " ", background_style
+                )
                 for first, wrapped_line in loop_first(wrapped_lines):
                     if first:
                         line_column = str(line_no).rjust(numbers_column_width - 2) + " "
@@ -653,7 +682,7 @@ class Syntax(JupyterMixin):
                             yield _Segment("  ", highlight_number_style)
                             yield _Segment(line_column, number_style)
                     else:
-                        yield padding
+                        yield wrapped_line_left_pad
                     yield from wrapped_line
                     yield new_line
             else:
@@ -739,6 +768,16 @@ if __name__ == "__main__":  # pragma: no cover
         dest="lexer_name",
         help="Lexer name",
     )
+    parser.add_argument(
+        "-p", "--padding", type=int, default=0, dest="padding", help="Padding"
+    )
+    parser.add_argument(
+        "--highlight-line",
+        type=int,
+        default=None,
+        dest="highlight_line",
+        help="The line number (not index!) to highlight",
+    )
     args = parser.parse_args()
 
     from rich.console import Console
@@ -755,6 +794,8 @@ if __name__ == "__main__":  # pragma: no cover
             theme=args.theme,
             background_color=args.background_color,
             indent_guides=args.indent_guides,
+            padding=args.padding,
+            highlight_lines={args.highlight_line},
         )
     else:
         syntax = Syntax.from_path(
@@ -765,5 +806,7 @@ if __name__ == "__main__":  # pragma: no cover
             theme=args.theme,
             background_color=args.background_color,
             indent_guides=args.indent_guides,
+            padding=args.padding,
+            highlight_lines={args.highlight_line},
         )
     console.print(syntax, soft_wrap=args.soft_wrap)

Test output

show
F
=================================== FAILURES ===================================
_______________________________ test_blank_lines _______________________________

    def test_blank_lines():
        code = "\n\nimport this\n\n"
        syntax = Syntax(
            code, lexer="python", theme="ascii_light", code_width=30, line_numbers=True
        )
        result = render(syntax)
        print(repr(result))
>       assert (
            result
            == "\x1b[1;38;2;24;24;24;48;2;248;248;248m  \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m1 \x1b[0m\x1b[48;2;248;248;248m                              \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m  \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m2 \x1b[0m\x1b[48;2;248;248;248m                              \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m  \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m3 \x1b[0m\x1b[1;38;2;0;128;0;48;2;248;248;248mimport\x1b[0m\x1b[38;2;0;0;0;48;2;248;248;248m \x1b[0m\x1b[1;38;2;0;0;255;48;2;248;248;248mthis\x1b[0m\x1b[48;2;248;248;248m                   \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m  \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m4 \x1b[0m\x1b[48;2;248;248;248m                              \x1b[0m\n\x1b[1;38;2;24;24;24;48;2;248;248;248m  \x1b[0m\x1b[38;2;173;173;173;48;2;248;248;248m5 \x1b[0m\x1b[48;2;248;248;248m                              \x1b[0m\n"
        )
E       AssertionError: assert '\x1b[1m  \x1...           \n' == '\x1b[1;38;2;...    \x1b[0m\n'
E         
E         -   1                               
E         -   2                               
E         -   3 import this                   
E         -   [38;2;17...
E         
E         ...Full output truncated (7 lines hidden), use '-vv' to show

tests/test_syntax.py:39: AssertionError
----------------------------- Captured stdout call -----------------------------
'\x1b[1m  \x1b[0m1                               \n\x1b[1m  \x1b[0m2                               \n\x1b[1m  \x1b[0m3 \x1b[1mimport\x1b[0m \x1b[1mthis\x1b[0m                   \n\x1b[1m  \x1b[0m4                               \n\x1b[1m  \x1b[0m5                               \n'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 failed in 0.17s