Skip to content

Commit 7dea1e7

Browse files
authored
Merge pull request #2342 from Textualize/rule-rich-measure
Rule rich measure
2 parents 4c554c5 + 9332476 commit 7dea1e7

File tree

6 files changed

+150
-15
lines changed

6 files changed

+150
-15
lines changed

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The following people have contributed to the development of Rich:
1212
- [Pete Davison](https://github.com/pd93)
1313
- [James Estevez](https://github.com/jstvz)
1414
- [Oleksis Fraga](https://github.com/oleksis)
15+
- [Andy Gimblett](https://github.com/gimbo)
1516
- [Michał Górny](https://github.com/mgorny)
1617
- [Leron Gray](https://github.com/daddycocoaman)
1718
- [Kenneth Hoste](https://github.com/boegel)

rich/cells.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def set_cell_size(text: str, total: int) -> str:
8080
return text + " " * (total - size)
8181
return text[:total]
8282

83-
if not total:
83+
if total <= 0:
8484
return ""
8585
cell_size = cell_len(text)
8686
if cell_size == total:

rich/rule.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .cells import cell_len, set_cell_size
55
from .console import Console, ConsoleOptions, RenderResult
66
from .jupyter import JupyterMixin
7+
from .measure import Measurement
78
from .style import Style
89
from .text import Text
910

@@ -62,10 +63,7 @@ def __rich_console__(
6263

6364
chars_len = cell_len(characters)
6465
if not self.title:
65-
rule_text = Text(characters * ((width // chars_len) + 1), self.style)
66-
rule_text.truncate(width)
67-
rule_text.plain = set_cell_size(rule_text.plain, width)
68-
yield rule_text
66+
yield self._rule_line(chars_len, width)
6967
return
7068

7169
if isinstance(self.title, Text):
@@ -75,10 +73,16 @@ def __rich_console__(
7573

7674
title_text.plain = title_text.plain.replace("\n", " ")
7775
title_text.expand_tabs()
78-
rule_text = Text(end=self.end)
7976

77+
required_space = 4 if self.align == "center" else 2
78+
truncate_width = max(0, width - required_space)
79+
if not truncate_width:
80+
yield self._rule_line(chars_len, width)
81+
return
82+
83+
rule_text = Text(end=self.end)
8084
if self.align == "center":
81-
title_text.truncate(width - 4, overflow="ellipsis")
85+
title_text.truncate(truncate_width, overflow="ellipsis")
8286
side_width = (width - cell_len(title_text.plain)) // 2
8387
left = Text(characters * (side_width // chars_len + 1))
8488
left.truncate(side_width - 1)
@@ -89,27 +93,42 @@ def __rich_console__(
8993
rule_text.append(title_text)
9094
rule_text.append(" " + right.plain, self.style)
9195
elif self.align == "left":
92-
title_text.truncate(width - 2, overflow="ellipsis")
96+
title_text.truncate(truncate_width, overflow="ellipsis")
9397
rule_text.append(title_text)
9498
rule_text.append(" ")
9599
rule_text.append(characters * (width - rule_text.cell_len), self.style)
96100
elif self.align == "right":
97-
title_text.truncate(width - 2, overflow="ellipsis")
101+
title_text.truncate(truncate_width, overflow="ellipsis")
98102
rule_text.append(characters * (width - title_text.cell_len - 1), self.style)
99103
rule_text.append(" ")
100104
rule_text.append(title_text)
101105

102106
rule_text.plain = set_cell_size(rule_text.plain, width)
103107
yield rule_text
104108

109+
def _rule_line(self, chars_len: int, width: int) -> Text:
110+
rule_text = Text(self.characters * ((width // chars_len) + 1), self.style)
111+
rule_text.truncate(width)
112+
rule_text.plain = set_cell_size(rule_text.plain, width)
113+
return rule_text
114+
115+
def __rich_measure__(
116+
self, console: Console, options: ConsoleOptions
117+
) -> Measurement:
118+
return Measurement(1, 1)
119+
105120

106121
if __name__ == "__main__": # pragma: no cover
107-
from rich.console import Console
108122
import sys
109123

124+
from rich.console import Console
125+
110126
try:
111127
text = sys.argv[1]
112128
except IndexError:
113129
text = "Hello, World"
114130
console = Console()
115131
console.print(Rule(title=text))
132+
133+
console = Console()
134+
console.print(Rule("foo"), width=4)

tests/test_rule.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,47 @@ def test_rule_cjk():
6161
assert console.file.getvalue() == expected
6262

6363

64+
@pytest.mark.parametrize(
65+
"align,outcome",
66+
[
67+
("center", "───\n"),
68+
("left", "… ─\n"),
69+
("right", "─ …\n"),
70+
],
71+
)
72+
def test_rule_not_enough_space_for_title_text(align, outcome):
73+
console = Console(width=3, file=io.StringIO(), record=True)
74+
console.rule("Hello!", align=align)
75+
assert console.file.getvalue() == outcome
76+
77+
78+
def test_rule_center_aligned_title_not_enough_space_for_rule():
79+
console = Console(width=4, file=io.StringIO(), record=True)
80+
console.rule("ABCD")
81+
assert console.file.getvalue() == "────\n"
82+
83+
84+
@pytest.mark.parametrize("align", ["left", "right"])
85+
def test_rule_side_aligned_not_enough_space_for_rule(align):
86+
console = Console(width=2, file=io.StringIO(), record=True)
87+
console.rule("ABCD", align=align)
88+
assert console.file.getvalue() == "──\n"
89+
90+
91+
@pytest.mark.parametrize(
92+
"align,outcome",
93+
[
94+
("center", "─ … ─\n"),
95+
("left", "AB… ─\n"),
96+
("right", "─ AB…\n"),
97+
],
98+
)
99+
def test_rule_just_enough_width_available_for_title(align, outcome):
100+
console = Console(width=5, file=io.StringIO(), record=True)
101+
console.rule("ABCD", align=align)
102+
assert console.file.getvalue() == outcome
103+
104+
64105
def test_characters():
65106
console = Console(
66107
width=16,

tests/test_rule_in_table.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import io
2+
from textwrap import dedent
3+
4+
import pytest
5+
6+
from rich import box
7+
from rich.console import Console
8+
from rich.rule import Rule
9+
from rich.table import Table
10+
11+
12+
@pytest.mark.parametrize("expand_kwarg", ({}, {"expand": False}))
13+
def test_rule_in_unexpanded_table(expand_kwarg):
14+
console = Console(width=32, file=io.StringIO(), legacy_windows=False, _environ={})
15+
table = Table(box=box.ASCII, show_header=False, **expand_kwarg)
16+
table.add_column()
17+
table.add_column()
18+
table.add_row("COL1", "COL2")
19+
table.add_row("COL1", Rule())
20+
table.add_row("COL1", "COL2")
21+
console.print(table)
22+
expected = dedent(
23+
"""\
24+
+-------------+
25+
| COL1 | COL2 |
26+
| COL1 | ──── |
27+
| COL1 | COL2 |
28+
+-------------+
29+
"""
30+
)
31+
result = console.file.getvalue()
32+
assert result == expected
33+
34+
35+
def test_rule_in_expanded_table():
36+
console = Console(width=32, file=io.StringIO(), legacy_windows=False, _environ={})
37+
table = Table(box=box.ASCII, expand=True, show_header=False)
38+
table.add_column()
39+
table.add_column()
40+
table.add_row("COL1", "COL2")
41+
table.add_row("COL1", Rule(style=None))
42+
table.add_row("COL1", "COL2")
43+
console.print(table)
44+
expected = dedent(
45+
"""\
46+
+------------------------------+
47+
| COL1 | COL2 |
48+
| COL1 | ──────────── |
49+
| COL1 | COL2 |
50+
+------------------------------+
51+
"""
52+
)
53+
result = console.file.getvalue()
54+
assert result == expected
55+
56+
57+
def test_rule_in_ratio_table():
58+
console = Console(width=32, file=io.StringIO(), legacy_windows=False, _environ={})
59+
table = Table(box=box.ASCII, expand=True, show_header=False)
60+
table.add_column(ratio=1)
61+
table.add_column()
62+
table.add_row("COL1", "COL2")
63+
table.add_row("COL1", Rule(style=None))
64+
table.add_row("COL1", "COL2")
65+
console.print(table)
66+
expected = dedent(
67+
"""\
68+
+------------------------------+
69+
| COL1 | COL2 |
70+
| COL1 | ──── |
71+
| COL1 | COL2 |
72+
+------------------------------+
73+
"""
74+
)
75+
result = console.file.getvalue()
76+
assert result == expected

tests/test_spinner.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,15 @@ def get_time():
4646
spinner = Spinner("dots")
4747
console.print(spinner)
4848

49-
spinner.update(text="Bar", style="green", speed=2)
50-
time += 80 / 1000
51-
console.print(spinner)
49+
rule = Rule("Bar")
5250

53-
spinner.update(text=Rule("Bar"))
51+
spinner.update(text=rule)
5452
time += 80 / 1000
5553
console.print(spinner)
5654

5755
result = console.end_capture()
5856
print(repr(result))
59-
expected = f"⠋\n\x1b[32m⠙\x1b[0m Bar\n\x1b[32m⠸\x1b[0m \x1b[92m────── \x1b[0mBar\x1b[92m ──────\x1b[0m\n"
57+
expected = "⠋\n\x1b[92m─\x1b[0m\n"
6058
assert result == expected
6159

6260

0 commit comments

Comments
 (0)