Skip to content

Commit 6d7ba58

Browse files
committed
rule fix
1 parent 8086c57 commit 6d7ba58

12 files changed

+125
-78
lines changed

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,21 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [2.0.0] - 2020-07-07
8+
## [2.0.1] - 2020-06-10
9+
10+
### Added
11+
12+
- Added expand option to Padding
13+
14+
### Changed
15+
16+
- Some minor optimizations in Text
17+
18+
### Fixed
19+
20+
- Fixed broken rule with CJK text
21+
22+
## [2.0.0] - 2020-06-06
923

1024
### Added
1125

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "rich"
33
homepage = "https://github.com/willmcgugan/rich"
44
documentation = "https://rich.readthedocs.io/en/latest/"
5-
version = "2.0.0"
5+
version = "2.0.1"
66
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
77
authors = ["Will McGugan <willmcgugan@gmail.com>"]
88
license = "MIT"

rich/__main__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,13 @@ def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
167167
if __name__ == "__main__": # pragma: no cover
168168
console = Console(file=io.StringIO(), force_terminal=True)
169169
test_card = make_test_card()
170+
171+
# Print once to warm cache
172+
console.print(test_card)
173+
console.file = io.StringIO()
174+
170175
start = process_time()
171176
console.print(test_card)
172-
taken = int((process_time() - start) * 1000.0)
177+
taken = round((process_time() - start) * 1000.0, 1)
173178
print(console.file.getvalue()) # type: ignore
174179
print(f"rendered in {taken}ms")

rich/_wrap.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,27 @@ def divide_line(text: str, width: int, fold: bool = True) -> List[int]:
2121
divides: List[int] = []
2222
append = divides.append
2323
line_position = 0
24-
24+
_cell_len = cell_len
2525
for start, _end, word in words(text):
26-
word_length = cell_len(word.rstrip())
26+
word_length = _cell_len(word.rstrip())
2727
if line_position + word_length > width:
2828
if word_length > width:
2929
if fold:
3030
for last, line in loop_last(
3131
chop_cells(word, width, position=line_position)
3232
):
3333
if last:
34-
line_position = cell_len(line)
34+
line_position = _cell_len(line)
3535
else:
3636
start += len(line)
3737
append(start)
3838
else:
39-
line_position = cell_len(word)
39+
line_position = _cell_len(word)
4040
elif line_position and start:
4141
append(start)
42-
line_position = cell_len(word)
42+
line_position = _cell_len(word)
4343
else:
44-
line_position += cell_len(word)
44+
line_position += _cell_len(word)
4545
return divides
4646

4747

rich/emoji.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,21 +58,18 @@ def __rich_console__(
5858

5959

6060
if __name__ == "__main__": # pragma: no cover
61-
from .console import Console
61+
import sys
6262

63-
c = Console(markup=False)
63+
from rich.columns import Columns
64+
from rich.console import Console
6465

65-
e = Emoji("thumbs_up")
66-
print(repr(e))
67-
print(e)
66+
console = Console(record=True)
6867

69-
c.print(Emoji("thumbs_up"))
70-
# c.print("Hello")
71-
c.print("Hello World")
72-
from .panel import Panel
68+
columns = Columns(
69+
(f":{name}: {name}" for name in sorted(EMOJI.keys()) if "\u200D" not in name),
70+
column_first=True,
71+
)
7372

74-
c.print(Panel(Emoji.replace("Hello, :smiley: ! :thumbs_up: :sfwdfwer:")))
75-
76-
c.print("Here is a :smiley: :link: Hello World")
77-
78-
c.print(":beer:")
73+
console.print(columns)
74+
if len(sys.argv) > 1:
75+
console.save_html(sys.argv[1])

rich/markup.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,15 @@ def render(markup: str, style: Union[str, Style] = "", emoji: bool = True) -> Te
8282
Returns:
8383
Text: A test instance.
8484
"""
85+
emoji_replace = _emoji_replace
86+
if "[" not in markup:
87+
return Text(emoji_replace(markup) if emoji else markup, style=style)
8588
text = Text(style=style)
8689
append = text.append
8790
normalize = Style.normalize
8891

8992
style_stack: List[Tuple[int, Tag]] = []
9093
pop = style_stack.pop
91-
emoji_replace = _emoji_replace
9294

9395
spans: List[Span] = []
9496
append_span = spans.append

rich/padding.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class Padding(JupyterMixin):
2828
pad (Union[int, Tuple[int]]): Padding for top, right, bottom, and left borders.
2929
May be specified with 1, 2, or 4 integers (CSS style).
3030
style (Union[str, Style], optional): Style for padding characters. Defaults to "none".
31+
expand (bool, optional): Expand padding to fit available width. Defaults to True.
3132
"""
3233

3334
def __init__(
@@ -36,10 +37,12 @@ def __init__(
3637
pad: "PaddingDimensions" = (0, 0, 0, 0),
3738
*,
3839
style: Union[str, Style] = "none",
40+
expand: bool = True,
3941
):
4042
self.renderable = renderable
4143
self.top, self.right, self.bottom, self.left = self.unpack(pad)
4244
self.style = style
45+
self.expand = expand
4346

4447
@classmethod
4548
def indent(cls, renderable: "RenderableType", level: int) -> "Padding":
@@ -53,7 +56,7 @@ def indent(cls, renderable: "RenderableType", level: int) -> "Padding":
5356
Padding: A Padding instance.
5457
"""
5558

56-
return Padding(renderable, pad=(0, 0, 0, level))
59+
return Padding(renderable, pad=(0, 0, 0, level), expand=False)
5760

5861
@staticmethod
5962
def unpack(pad: "PaddingDimensions") -> Tuple[int, int, int, int]:
@@ -79,7 +82,15 @@ def __rich_console__(
7982
) -> "RenderResult":
8083

8184
style = console.get_style(self.style)
82-
width = options.max_width
85+
if self.expand:
86+
width = options.max_width
87+
else:
88+
width = min(
89+
Measurement.get(console, self.renderable, options.max_width).maximum
90+
+ self.left
91+
+ self.right,
92+
options.max_width,
93+
)
8394
child_options = options.update(width=width - self.left - self.right)
8495
lines = console.render_lines(self.renderable, child_options, style=style)
8596
lines = Segment.set_shape(lines, child_options.max_width, style=style)

rich/rule.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Union
22

3+
from .cells import cell_len
34
from .console import Console, ConsoleOptions, RenderResult
45
from .jupyter import JupyterMixin
56
from .segment import Segment
@@ -21,10 +22,8 @@ def __init__(
2122
character: str = None,
2223
style: Union[str, Style] = "rule.line",
2324
) -> None:
24-
if character and len(character) != 1:
25-
raise ValueError(
26-
"Rule requires character argument to be a string of length 1"
27-
)
25+
if character and cell_len(character) != 1:
26+
raise ValueError("'character' argument must have a cell width of 1")
2827
self.title = title
2928
self.character = character
3029
self.style = style
@@ -46,14 +45,17 @@ def __rich_console__(
4645
title_text = self.title
4746
else:
4847
title_text = console.render_str(self.title, style="rule.text")
49-
if len(title_text) > width - 4:
48+
49+
if cell_len(title_text.plain) > width - 4:
5050
title_text.set_length(width - 4)
5151

5252
rule_text = Text()
53-
center = (width - len(title_text)) // 2
53+
center = (width - cell_len(title_text.plain)) // 2
5454
rule_text.append(character * (center - 1) + " ", self.style)
5555
rule_text.append(title_text)
56-
rule_text.append(" " + character * (width - len(rule_text) - 1), self.style)
56+
rule_text.append(
57+
" " + character * (width - cell_len(rule_text.plain) - 1), self.style
58+
)
5759
yield rule_text
5860

5961

rich/style.py

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from binascii import crc32
2-
from functools import lru_cache
2+
from functools import lru_cache, reduce
3+
from operator import or_
34
import sys
45
from typing import Any, Dict, Iterable, List, Mapping, Optional, Type, Union
56

@@ -110,35 +111,39 @@ def _make_color(color: Union[Color, str]) -> Color:
110111

111112
self._color = None if color is None else _make_color(color)
112113
self._bgcolor = None if bgcolor is None else _make_color(bgcolor)
113-
self._attributes = (
114-
(bold or 0)
115-
| (dim or 0) << 1
116-
| (italic or 0) << 2
117-
| (underline or 0) << 3
118-
| (blink or 0) << 4
119-
| (blink2 or 0) << 5
120-
| (reverse or 0) << 6
121-
| (conceal or 0) << 7
122-
| (strike or 0) << 8
123-
| (underline2 or 0) << 9
124-
| (frame or 0) << 10
125-
| (encircle or 0) << 11
126-
| (overline or 0) << 12
114+
self._attributes = sum(
115+
(
116+
bold and 1 or 0,
117+
dim and 2 or 0,
118+
italic and 4 or 0,
119+
underline and 8 or 0,
120+
blink and 16 or 0,
121+
blink2 and 32 or 0,
122+
reverse and 64 or 0,
123+
conceal and 128 or 0,
124+
strike and 256 or 0,
125+
underline2 and 512 or 0,
126+
frame and 1024 or 0,
127+
encircle and 2048 or 0,
128+
overline and 4096 or 0,
129+
)
127130
)
128-
self._set_attributes = (
129-
(bold is not None)
130-
| (dim is not None) << 1
131-
| (italic is not None) << 2
132-
| (underline is not None) << 3
133-
| (blink is not None) << 4
134-
| (blink2 is not None) << 5
135-
| (reverse is not None) << 6
136-
| (conceal is not None) << 7
137-
| (strike is not None) << 8
138-
| (underline2 is not None) << 9
139-
| (frame is not None) << 10
140-
| (encircle is not None) << 11
141-
| (overline is not None) << 12
131+
self._set_attributes = sum(
132+
(
133+
bold is not None,
134+
dim is not None and 2,
135+
italic is not None and 4,
136+
underline is not None and 8,
137+
blink is not None and 16,
138+
blink2 is not None and 32,
139+
reverse is not None and 64,
140+
conceal is not None and 128,
141+
strike is not None and 256,
142+
underline2 is not None and 512,
143+
frame is not None and 1024,
144+
encircle is not None and 2048,
145+
overline is not None and 4096,
146+
)
142147
)
143148
self._link = link
144149

rich/text.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,18 @@ class Text(JupyterMixin):
109109
tab_size (int): Number of spaces per tab, or ``None`` to use ``console.tab_size``. Defaults to 8.
110110
"""
111111

112+
__slots__ = [
113+
"_text",
114+
"style",
115+
"justify",
116+
"overflow",
117+
"no_wrap",
118+
"end",
119+
"tab_size",
120+
"_spans",
121+
"_length",
122+
]
123+
112124
def __init__(
113125
self,
114126
text: str = "",
@@ -121,15 +133,14 @@ def __init__(
121133
tab_size: Optional[int] = 8,
122134
spans: List[Span] = None,
123135
) -> None:
124-
text = strip_control_codes(text)
125-
self._text: List[str] = [text] if text else []
136+
self._text = [strip_control_codes(text)]
126137
self.style = style
127138
self.justify = justify
128139
self.overflow = overflow
129140
self.no_wrap = no_wrap
130141
self.end = end
131142
self.tab_size = tab_size
132-
self._spans: List[Span] = spans if spans is not None else []
143+
self._spans: List[Span] = spans or []
133144
self._length: int = len(text)
134145

135146
def __len__(self) -> int:
@@ -229,8 +240,7 @@ def assemble(
229240
def plain(self) -> str:
230241
"""Get the text as a single string."""
231242
if len(self._text) != 1:
232-
text = "".join(self._text)
233-
self._text[:] = [text]
243+
self._text[:] = ["".join(self._text)]
234244
return self._text[0]
235245

236246
@plain.setter
@@ -245,7 +255,7 @@ def plain(self, new_text: str) -> None:
245255

246256
@property
247257
def spans(self) -> List[Span]:
248-
"""Get a copy of the list of spans."""
258+
"""Get a reference to the internal list of spans."""
249259
return self._spans
250260

251261
@spans.setter
@@ -412,13 +422,7 @@ def set_length(self, new_length: int) -> None:
412422
if length < new_length:
413423
self.pad_right(new_length - length)
414424
else:
415-
text = self.plain[:new_length]
416-
self.plain = text
417-
new_spans = []
418-
for span in self._spans:
419-
if span.start < new_length:
420-
new_spans.append(span.right_crop(new_length))
421-
self._spans[:] = new_spans
425+
self.plain = self.plain[:new_length]
422426

423427
def __rich_console__(
424428
self, console: "Console", options: "ConsoleOptions"

tests/test_rule.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ def test_rule():
1717
assert console.file.getvalue() == expected
1818

1919

20+
def test_rule_cjk():
21+
console = Console(
22+
width=16, file=io.StringIO(), force_terminal=True, color_system=None
23+
)
24+
console.rule("欢迎!")
25+
expected = "──── 欢迎! ────\n"
26+
assert console.file.getvalue() == expected
27+
28+
2029
def test_repr():
2130
rule = Rule("foo")
2231
assert isinstance(repr(rule), str)

tools/make_emoji.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88

99
from emoji.unicode_codes import EMOJI_ALIAS_UNICODE
1010

11-
emoji = {
12-
k.lower().strip(":"): normalize("NFC", v) for k, v in EMOJI_ALIAS_UNICODE.items()
13-
}
11+
emoji = {k.lower().strip(":"): v for k, v in EMOJI_ALIAS_UNICODE.items()}
1412

1513
with open("_emoji_codes.py", "wt") as f:
1614
f.write("EMOJI=" + str(emoji))

0 commit comments

Comments
 (0)