From 701308c19a0f64921285a302d6129d103e9173a4 Mon Sep 17 00:00:00 2001 From: Roy Attias Date: Fri, 3 May 2024 01:07:52 +0300 Subject: [PATCH 1/4] widgets: text-area: Make the starting line number a kwarg --- src/textual/widgets/_text_area.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/textual/widgets/_text_area.py b/src/textual/widgets/_text_area.py index 21768f2bb8..7b1c10cb6b 100644 --- a/src/textual/widgets/_text_area.py +++ b/src/textual/widgets/_text_area.py @@ -301,6 +301,9 @@ class TextArea(ScrollView): Changing this value will immediately re-render the `TextArea`.""" + line_number_start: Reactive[int] = reactive(1, init=False) + """The line number the first line should be.""" + indent_width: Reactive[int] = reactive(4, init=False) """The width of tabs or the multiple of spaces to align to on pressing the `tab` key. @@ -370,6 +373,7 @@ def __init__( tab_behavior: Literal["focus", "indent"] = "focus", read_only: bool = False, show_line_numbers: bool = False, + line_number_start: int = 1, max_checkpoints: int = 50, name: str | None = None, id: str | None = None, @@ -387,6 +391,7 @@ def __init__( tab_behavior: If 'focus', pressing tab will switch focus. If 'indent', pressing tab will insert a tab. read_only: Enable read-only mode. This prevents edits using the keyboard. show_line_numbers: Show line numbers on the left edge. + line_number_start: What line number to start on. max_checkpoints: The maximum number of undo history checkpoints to retain. name: The name of the `TextArea` widget. id: The ID of the widget, used to refer to it from Textual CSS. @@ -455,6 +460,7 @@ def __init__( self.set_reactive(TextArea.soft_wrap, soft_wrap) self.set_reactive(TextArea.read_only, read_only) self.set_reactive(TextArea.show_line_numbers, show_line_numbers) + self.set_reactive(TextArea.line_number_start, line_number_start) self.tab_behavior = tab_behavior @@ -475,6 +481,7 @@ def code_editor( tab_behavior: Literal["focus", "indent"] = "indent", read_only: bool = False, show_line_numbers: bool = True, + line_number_start: int = 1, max_checkpoints: int = 50, name: str | None = None, id: str | None = None, @@ -494,6 +501,7 @@ def code_editor( soft_wrap: Enable soft wrapping. tab_behavior: If 'focus', pressing tab will switch focus. If 'indent', pressing tab will insert a tab. show_line_numbers: Show line numbers on the left edge. + line_number_start: What line number to start on. name: The name of the `TextArea` widget. id: The ID of the widget, used to refer to it from Textual CSS. classes: One or more Textual CSS compatible class names separated by spaces. @@ -508,6 +516,7 @@ def code_editor( tab_behavior=tab_behavior, read_only=read_only, show_line_numbers=show_line_numbers, + line_number_start=line_number_start, max_checkpoints=max_checkpoints, name=name, id=id, @@ -691,6 +700,11 @@ def _watch_show_line_numbers(self) -> None: self._rewrap_and_refresh_virtual_size() self.scroll_cursor_visible() + def _watch_line_number_start(self) -> None: + """The line number gutter max size might change and contributes to virtual size, so recalculate.""" + self._rewrap_and_refresh_virtual_size() + self.scroll_cursor_visible() + def _watch_indent_width(self) -> None: """Changing width of tabs will change the document display width.""" self._rewrap_and_refresh_virtual_size() @@ -1142,7 +1156,9 @@ def render_line(self, y: int) -> Strip: gutter_style = theme.gutter_style gutter_width_no_margin = gutter_width - 2 - gutter_content = str(line_index + 1) if section_offset == 0 else "" + gutter_content = ( + str(line_index + self.line_number_start) if section_offset == 0 else "" + ) gutter = Text( f"{gutter_content:>{gutter_width_no_margin}} ", style=gutter_style or "", @@ -1467,7 +1483,8 @@ def gutter_width(self) -> int: # The longest number in the gutter plus two extra characters: `│ `. gutter_margin = 2 gutter_width = ( - len(str(self.document.line_count)) + gutter_margin + len(str(self.document.line_count - 1 + self.line_number_start)) + + gutter_margin if self.show_line_numbers else 0 ) From 052373176e051122a90870619f7457a384c7ac15 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 11 Jul 2024 11:50:30 +0100 Subject: [PATCH 2/4] Add snapshot test for TextArea.line_number_start and update docs --- docs/widgets/text_area.md | 3 + .../__snapshots__/test_snapshots.ambr | 91 +++++++++++++++++++ .../text_area_line_number_start.py | 25 +++++ tests/snapshot_tests/test_snapshots.py | 6 ++ 4 files changed, 125 insertions(+) create mode 100644 tests/snapshot_tests/snapshot_apps/text_area_line_number_start.py diff --git a/docs/widgets/text_area.md b/docs/widgets/text_area.md index 5747d394ec..0824f4c697 100644 --- a/docs/widgets/text_area.md +++ b/docs/widgets/text_area.md @@ -353,6 +353,8 @@ the `show_line_numbers` attribute to `True` or `False`. Setting this attribute will immediately repaint the `TextArea` to reflect the new value. +You can also change the start line number (the topmost line number in the gutter) by setting the `line_number_start` reactive attribute. + ### Extending `TextArea` Sometimes, you may wish to subclass `TextArea` to add some extra functionality. @@ -506,6 +508,7 @@ A detailed view of these classes is out of scope, but do note that a lot of the | `theme` | `str` | `"css"` | The theme to use. | | `selection` | `Selection` | `Selection()` | The current selection. | | `show_line_numbers` | `bool` | `False` | Show or hide line numbers. | +| `line_number_start` | `int` | `1` | The start line number in the gutter. | | `indent_width` | `int` | `4` | The number of spaces to indent and width of tabs. | | `match_cursor_bracket` | `bool` | `True` | Enable/disable highlighting matching brackets under cursor. | | `cursor_blink` | `bool` | `True` | Enable/disable blinking of the cursor when the widget has focus. | diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index d39d170cce..23eadf7e01 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -44812,6 +44812,97 @@ ''' # --- +# name: test_text_area_line_number_start + ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + LineNumbersReactive + + + + + + + + + + + + + + + + + + + + + + ''' +# --- # name: test_text_area_read_only_cursor_rendering ''' diff --git a/tests/snapshot_tests/snapshot_apps/text_area_line_number_start.py b/tests/snapshot_tests/snapshot_apps/text_area_line_number_start.py new file mode 100644 index 0000000000..d8db335960 --- /dev/null +++ b/tests/snapshot_tests/snapshot_apps/text_area_line_number_start.py @@ -0,0 +1,25 @@ +from textual.app import App, ComposeResult +from textual.widgets import TextArea + +TEXT = """\ +Foo +Bar +Baz +""" + + +class LineNumbersReactive(App[None]): + START_LINE_NUMBER = 9999 + + def compose(self) -> ComposeResult: + yield TextArea( + TEXT, + soft_wrap=True, + show_line_numbers=True, + line_number_start=self.START_LINE_NUMBER, + ) + + +app = LineNumbersReactive() +if __name__ == "__main__": + app.run() diff --git a/tests/snapshot_tests/test_snapshots.py b/tests/snapshot_tests/test_snapshots.py index 07c8d4eb27..7421f99d48 100644 --- a/tests/snapshot_tests/test_snapshots.py +++ b/tests/snapshot_tests/test_snapshots.py @@ -1000,6 +1000,12 @@ def test_text_area_wrapping_and_folding(snap_compare): ) +def test_text_area_line_number_start(snap_compare): + assert snap_compare( + SNAPSHOT_APPS_DIR / "text_area_line_number_start.py", terminal_size=(32, 8) + ) + + def test_digits(snap_compare) -> None: assert snap_compare(SNAPSHOT_APPS_DIR / "digits.py") From bcd22a8c380437526c9711367edc3b6620cbfa10 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 11 Jul 2024 11:59:37 +0100 Subject: [PATCH 3/4] Update snapshots --- .../__snapshots__/test_snapshots.ambr | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 23eadf7e01..35a0aa0d48 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -44835,68 +44835,74 @@ font-weight: 700; } - .terminal-2322095458-matrix { + .terminal-3222584256-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-2322095458-title { + .terminal-3222584256-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-2322095458-r1 { fill: #1e1e1e } - .terminal-2322095458-r2 { fill: #c5c8c6 } + .terminal-3222584256-r1 { fill: #1e1e1e } + .terminal-3222584256-r2 { fill: #0178d4 } + .terminal-3222584256-r3 { fill: #c5c8c6 } + .terminal-3222584256-r4 { fill: #a8a8a8;font-weight: bold } + .terminal-3222584256-r5 { fill: #151515 } + .terminal-3222584256-r6 { fill: #e2e2e2 } + .terminal-3222584256-r7 { fill: #787878 } + .terminal-3222584256-r8 { fill: #e1e1e1 } - + - + - + - + - + - + - + - + - LineNumbersReactive + LineNumbersReactive - - - - - - - - - - - + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +  9999  Foo                   + 10000  Bar  + 10001  Baz  + 10002   + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ From c81059818b1004fdf301c8fc9f39df7d1d08a581 Mon Sep 17 00:00:00 2001 From: Darren Burns Date: Thu, 11 Jul 2024 12:18:14 +0100 Subject: [PATCH 4/4] Update snapshots using latest Textual version --- .../__snapshots__/test_snapshots.ambr | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr index 4be88eb693..e17165d8c8 100644 --- a/tests/snapshot_tests/__snapshots__/test_snapshots.ambr +++ b/tests/snapshot_tests/__snapshots__/test_snapshots.ambr @@ -45485,74 +45485,74 @@ font-weight: 700; } - .terminal-3222584256-matrix { + .terminal-3789936591-matrix { font-family: Fira Code, monospace; font-size: 20px; line-height: 24.4px; font-variant-east-asian: full-width; } - .terminal-3222584256-title { + .terminal-3789936591-title { font-size: 18px; font-weight: bold; font-family: arial; } - .terminal-3222584256-r1 { fill: #1e1e1e } - .terminal-3222584256-r2 { fill: #0178d4 } - .terminal-3222584256-r3 { fill: #c5c8c6 } - .terminal-3222584256-r4 { fill: #a8a8a8;font-weight: bold } - .terminal-3222584256-r5 { fill: #151515 } - .terminal-3222584256-r6 { fill: #e2e2e2 } - .terminal-3222584256-r7 { fill: #787878 } - .terminal-3222584256-r8 { fill: #e1e1e1 } + .terminal-3789936591-r1 { fill: #1e1e1e } + .terminal-3789936591-r2 { fill: #0178d4 } + .terminal-3789936591-r3 { fill: #c5c8c6 } + .terminal-3789936591-r4 { fill: #a8a8a8;font-weight: bold } + .terminal-3789936591-r5 { fill: #151515 } + .terminal-3789936591-r6 { fill: #e2e2e2 } + .terminal-3789936591-r7 { fill: #787878 } + .terminal-3789936591-r8 { fill: #e1e1e1 } - + - + - + - + - + - + - + - + - LineNumbersReactive + LineNumbersReactive - - - - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ -  9999  Foo                   - 10000  Bar  - 10001  Baz  - 10002   - - - ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ + + + + ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎ +  9999  Foo                   + 10000  Bar                   + 10001  Baz                   + 10002   + + + ▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎