Replacing some uses of cell_len with Cells.measure

This commit is contained in:
Darren Burns 2022-01-11 10:40:49 +00:00
parent d4450d9e43
commit 6139a0a6f3
7 changed files with 79 additions and 35 deletions

View File

@ -7,7 +7,6 @@ repos:
- id: check-ast
- id: check-builtin-literals
- id: check-case-conflict
- id: check-docstring-first
- id: check-merge-conflict
- id: check-json
- id: check-toml

View File

@ -33,6 +33,7 @@ pygments = "^2.6.0"
commonmark = "^0.9.0"
colorama = "^0.4.0"
ipywidgets = { version = "^7.5.1", optional = true }
cells = { path = "../cells/" }
[tool.poetry.extras]

View File

@ -1,8 +1,10 @@
import re
from typing import Iterable, List, Tuple
from .cells import cell_len, chop_cells
from cells.core import Cells
from ._loop import loop_last
from .cells import cell_len, chop_cells
re_word = re.compile(r"\s*\S+\s*")
@ -17,13 +19,13 @@ def words(text: str) -> Iterable[Tuple[int, int, str]]:
word_match = re_word.match(text, end)
def divide_line(text: str, width: int, fold: bool = True) -> List[int]:
def divide_line(text: str, width: int, cells: Cells, fold: bool = True) -> List[int]:
divides: List[int] = []
append = divides.append
line_position = 0
_cell_len = cell_len
_cell_width = cells.measure
for start, _end, word in words(text):
word_length = _cell_len(word.rstrip())
word_length = _cell_width(word.rstrip())
if line_position + word_length > width:
if word_length > width:
if fold:
@ -31,19 +33,19 @@ def divide_line(text: str, width: int, fold: bool = True) -> List[int]:
chop_cells(word, width, position=line_position)
):
if last:
line_position = _cell_len(line)
line_position = _cell_width(line)
else:
start += len(line)
append(start)
else:
if start:
append(start)
line_position = _cell_len(word)
line_position = _cell_width(word)
elif line_position and start:
append(start)
line_position = _cell_len(word)
line_position = _cell_width(word)
else:
line_position += _cell_len(word)
line_position += _cell_width(word)
return divides

View File

@ -11,9 +11,9 @@ from getpass import getpass
from html import escape
from inspect import isclass
from itertools import islice
from time import monotonic
from threading import RLock
from types import FrameType, TracebackType, ModuleType
from time import monotonic
from types import FrameType, ModuleType, TracebackType
from typing import (
IO,
TYPE_CHECKING,
@ -32,6 +32,8 @@ from typing import (
cast,
)
from cells.core import Cells
if sys.version_info >= (3, 8):
from typing import Literal, Protocol, runtime_checkable
else:
@ -71,6 +73,25 @@ if TYPE_CHECKING:
WINDOWS = platform.system() == "Windows"
DEFAULT_UNICODE_VERSION = "9.0.0"
UnicodeVersion = Literal[
"4.1.0",
"5.0.0",
"5.1.0",
"5.2.0",
"6.0.0",
"6.1.0",
"6.2.0",
"6.3.0",
"7.0.0",
"8.0.0",
"9.0.0",
"10.0.0",
"11.0.0",
"12.0.0",
"12.1.0",
"13.0.0",
]
HighlighterType = Callable[[Union[str, "Text"]], "Text"]
JustifyMethod = Literal["default", "left", "center", "right", "full"]
OverflowMethod = Literal["fold", "crop", "ellipsis", "ignore"]
@ -616,6 +637,10 @@ class Console:
get_datetime (Callable[[], datetime], optional): Callable that gets the current time as a datetime.datetime object (used by Console.log),
or None for datetime.now.
get_time (Callable[[], time], optional): Callable that gets the current time in seconds, default uses time.monotonic.
unicode_version (UnicodeVersion, optional): Version of the Unicode database to use.
If ``None``, the default ``"9.0.0"`` will be used.
cells (Cells, optional): ``Cells`` instance to use for character cell width measurement. If ``None``, Rich will instantiate
``Cell`` internally using the specified ``unicode_version``.
"""
_environ: Mapping[str, str] = os.environ
@ -652,6 +677,8 @@ class Console:
safe_box: bool = True,
get_datetime: Optional[Callable[[], datetime]] = None,
get_time: Optional[Callable[[], float]] = None,
unicode_version: Optional[UnicodeVersion] = DEFAULT_UNICODE_VERSION,
cells: Optional[Cells] = None,
_environ: Optional[Mapping[str, str]] = None,
):
# Copy of os.environ allows us to replace it for testing
@ -713,6 +740,7 @@ class Console:
self.safe_box = safe_box
self.get_datetime = get_datetime or datetime.now
self.get_time = get_time or monotonic
self.cells = cells or Cells(unicode_version or DEFAULT_UNICODE_VERSION)
self.style = style
self.no_color = (
no_color if no_color is not None else "NO_COLOR" in self._environ

View File

@ -1,13 +1,13 @@
from itertools import zip_longest
from typing import (
Iterator,
TYPE_CHECKING,
Iterable,
Iterator,
List,
Optional,
TypeVar,
Union,
overload,
TypeVar,
TYPE_CHECKING,
)
if TYPE_CHECKING:
@ -21,7 +21,6 @@ if TYPE_CHECKING:
)
from .text import Text
from .cells import cell_len
from .measure import Measurement
T = TypeVar("T")
@ -126,6 +125,7 @@ class Lines:
"""
from .text import Text
_cell_width = console.cells.measure
if justify == "left":
for line in self._lines:
line.truncate(width, overflow=overflow, pad=True)
@ -133,19 +133,19 @@ class Lines:
for line in self._lines:
line.rstrip()
line.truncate(width, overflow=overflow)
line.pad_left((width - cell_len(line.plain)) // 2)
line.pad_right(width - cell_len(line.plain))
line.pad_left((width - _cell_width(line.plain)) // 2)
line.pad_right(width - _cell_width(line.plain))
elif justify == "right":
for line in self._lines:
line.rstrip()
line.truncate(width, overflow=overflow)
line.pad_left(width - cell_len(line.plain))
line.pad_left(width - _cell_width(line.plain))
elif justify == "full":
for line_index, line in enumerate(self._lines):
if line_index == len(self._lines) - 1:
break
words = line.split(" ")
words_size = sum(cell_len(word.plain) for word in words)
words_size = sum(_cell_width(word.plain) for word in words)
num_spaces = len(words) - 1
spaces = [1 for _ in range(num_spaces)]
index = 0

View File

@ -1,28 +1,31 @@
import builtins
import dataclasses
import os
from rich.repr import RichReprResult
import re
import sys
from array import array
from collections import Counter, defaultdict, deque, UserDict, UserList
import dataclasses
from collections import Counter, UserDict, UserList, defaultdict, deque
from dataclasses import dataclass, fields, is_dataclass
from inspect import isclass
from itertools import islice
import re
from types import MappingProxyType
from typing import (
DefaultDict,
TYPE_CHECKING,
Any,
Callable,
DefaultDict,
Dict,
Iterable,
List,
Optional,
Set,
Union,
Tuple,
Union,
)
from types import MappingProxyType
from cells.core import Cells
from rich.repr import RichReprResult
try:
import attr as _attr_module
@ -30,12 +33,10 @@ except ImportError: # pragma: no cover
_attr_module = None # type: ignore
from .highlighter import ReprHighlighter
from . import get_console
from ._loop import loop_last
from ._pick import pick_bool
from .abc import RichRenderable
from .cells import cell_len
from .highlighter import ReprHighlighter
from .jupyter import JupyterMixin, JupyterRenderable
from .measure import Measurement
@ -278,8 +279,11 @@ class Pretty(JupyterMixin):
max_length=self.max_length,
max_string=self.max_string,
)
_cell_width = console.cells.measure
text_width = (
max(cell_len(line) for line in pretty_str.splitlines()) if pretty_str else 0
max(_cell_width(line) for line in pretty_str.splitlines())
if pretty_str
else 0
)
return Measurement(text_width, text_width)
@ -339,6 +343,10 @@ class Node:
children: Optional[List["Node"]] = None
key_separator = ": "
separator: str = ", "
cells: Optional[Cells] = None
def __post_init__(self):
self.cells = self.cells or Cells()
def iter_tokens(self) -> Iterable[str]:
"""Generate tokens for this node."""
@ -373,8 +381,9 @@ class Node:
bool: True if the node can be rendered within max length, otherwise False.
"""
total_length = start_length
_cell_width = self.cells.measure
for token in self.iter_tokens():
total_length += cell_len(token)
total_length += _cell_width(token)
if total_length > max_length:
return False
return True
@ -401,7 +410,7 @@ class Node:
while line_no < len(lines):
line = lines[line_no]
if line.expandable and not line.expanded:
if expand_all or not line.check_length(max_width):
if expand_all or not line.check_length(max_width, self.cells):
lines[line_no : line_no + 1] = line.expand(indent_size)
line_no += 1
@ -427,10 +436,11 @@ class _Line:
"""Check if the line may be expanded."""
return bool(self.node is not None and self.node.children)
def check_length(self, max_length: int) -> bool:
def check_length(self, max_length: int, cells: Cells) -> bool:
"""Check this line fits within a given number of cells."""
_cell_width = cells.measure
start_length = (
len(self.whitespace) + cell_len(self.text) + cell_len(self.suffix)
len(self.whitespace) + _cell_width(self.text) + _cell_width(self.suffix)
)
assert self.node is not None
return self.node.check_length(start_length, max_length)

View File

@ -2,7 +2,6 @@ import re
from functools import partial, reduce
from math import gcd
from operator import itemgetter
from rich.emoji import EmojiVariant
from typing import (
TYPE_CHECKING,
Any,
@ -16,6 +15,8 @@ from typing import (
Union,
)
from rich.emoji import EmojiVariant
from ._loop import loop_last
from ._pick import pick_bool
from ._wrap import divide_line
@ -1148,6 +1149,7 @@ class Text(JupyterMixin):
no_wrap = pick_bool(no_wrap, self.no_wrap, False) or overflow == "ignore"
cells = console.cells
lines = Lines()
for line in self.split(allow_blank=True):
if "\t" in line:
@ -1155,7 +1157,9 @@ class Text(JupyterMixin):
if no_wrap:
new_lines = Lines([line])
else:
offsets = divide_line(str(line), width, fold=wrap_overflow == "fold")
offsets = divide_line(
str(line), width, cells, fold=wrap_overflow == "fold"
)
new_lines = line.divide(offsets)
for line in new_lines:
line.rstrip_end(width)