mirror of
https://github.com/Textualize/rich.git
synced 2025-08-25 12:44:12 +00:00
commit
3f6625365a
18 changed files with 789 additions and 60 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@
|
|||
mypy_report
|
||||
docs/build
|
||||
docs/source/_build
|
||||
tools/*.txt
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.8.0] - 2020-03-17
|
||||
|
||||
### Added
|
||||
|
||||
- CJK support
|
||||
- Console level highlight flag
|
||||
- Added encoding argument to Syntax.from_path
|
||||
|
||||
### Changed
|
||||
|
||||
- Dropped support for Windows command prompt (try https://www.microsoft.com/en-gb/p/windows-terminal-preview/)
|
||||
- Added task_id to Progress.track
|
||||
|
||||
## [0.7.2] - 2020-03-15
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
name = "rich"
|
||||
homepage = "https://github.com/willmcgugan/rich"
|
||||
documentation = "https://rich.readthedocs.io/en/latest/"
|
||||
version = "0.7.2"
|
||||
version = "0.8.0-alpha.1"
|
||||
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||
authors = ["Will McGugan <willmcgugan@gmail.com>"]
|
||||
license = "MIT"
|
||||
|
@ -25,7 +25,6 @@ python = "^3.6"
|
|||
pprintpp = "^0.4.0"
|
||||
typing-extensions = "^3.7.4"
|
||||
dataclasses = {version="^0.7", python = "~3.6"}
|
||||
colorama = "^0.4.3"
|
||||
pygments = "^2.5.0"
|
||||
commonmark = "^0.9.0"
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
from colorama import init
|
||||
|
||||
from typing import Any, IO, Optional, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -22,7 +20,5 @@ def print(
|
|||
return write_console.print(*objects, sep=sep, end=end)
|
||||
|
||||
|
||||
init()
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
print("Hello, **World**")
|
||||
|
|
407
rich/_cell_widths.py
Normal file
407
rich/_cell_widths.py
Normal file
|
@ -0,0 +1,407 @@
|
|||
# Auto generated by make_terminal_widths.py
|
||||
|
||||
CELL_WIDTHS = [
|
||||
(0, 0, 0),
|
||||
(1, 31, -1),
|
||||
(127, 159, -1),
|
||||
(768, 879, 0),
|
||||
(1155, 1161, 0),
|
||||
(1425, 1469, 0),
|
||||
(1471, 1471, 0),
|
||||
(1473, 1474, 0),
|
||||
(1476, 1477, 0),
|
||||
(1479, 1479, 0),
|
||||
(1552, 1562, 0),
|
||||
(1611, 1631, 0),
|
||||
(1648, 1648, 0),
|
||||
(1750, 1756, 0),
|
||||
(1759, 1764, 0),
|
||||
(1767, 1768, 0),
|
||||
(1770, 1773, 0),
|
||||
(1809, 1809, 0),
|
||||
(1840, 1866, 0),
|
||||
(1958, 1968, 0),
|
||||
(2027, 2035, 0),
|
||||
(2070, 2073, 0),
|
||||
(2075, 2083, 0),
|
||||
(2085, 2087, 0),
|
||||
(2089, 2093, 0),
|
||||
(2137, 2139, 0),
|
||||
(2260, 2273, 0),
|
||||
(2275, 2306, 0),
|
||||
(2362, 2362, 0),
|
||||
(2364, 2364, 0),
|
||||
(2369, 2376, 0),
|
||||
(2381, 2381, 0),
|
||||
(2385, 2391, 0),
|
||||
(2402, 2403, 0),
|
||||
(2433, 2433, 0),
|
||||
(2492, 2492, 0),
|
||||
(2497, 2500, 0),
|
||||
(2509, 2509, 0),
|
||||
(2530, 2531, 0),
|
||||
(2561, 2562, 0),
|
||||
(2620, 2620, 0),
|
||||
(2625, 2626, 0),
|
||||
(2631, 2632, 0),
|
||||
(2635, 2637, 0),
|
||||
(2641, 2641, 0),
|
||||
(2672, 2673, 0),
|
||||
(2677, 2677, 0),
|
||||
(2689, 2690, 0),
|
||||
(2748, 2748, 0),
|
||||
(2753, 2757, 0),
|
||||
(2759, 2760, 0),
|
||||
(2765, 2765, 0),
|
||||
(2786, 2787, 0),
|
||||
(2817, 2817, 0),
|
||||
(2876, 2876, 0),
|
||||
(2879, 2879, 0),
|
||||
(2881, 2884, 0),
|
||||
(2893, 2893, 0),
|
||||
(2902, 2902, 0),
|
||||
(2914, 2915, 0),
|
||||
(2946, 2946, 0),
|
||||
(3008, 3008, 0),
|
||||
(3021, 3021, 0),
|
||||
(3072, 3072, 0),
|
||||
(3134, 3136, 0),
|
||||
(3142, 3144, 0),
|
||||
(3146, 3149, 0),
|
||||
(3157, 3158, 0),
|
||||
(3170, 3171, 0),
|
||||
(3201, 3201, 0),
|
||||
(3260, 3260, 0),
|
||||
(3263, 3263, 0),
|
||||
(3270, 3270, 0),
|
||||
(3276, 3277, 0),
|
||||
(3298, 3299, 0),
|
||||
(3329, 3329, 0),
|
||||
(3393, 3396, 0),
|
||||
(3405, 3405, 0),
|
||||
(3426, 3427, 0),
|
||||
(3530, 3530, 0),
|
||||
(3538, 3540, 0),
|
||||
(3542, 3542, 0),
|
||||
(3633, 3633, 0),
|
||||
(3636, 3642, 0),
|
||||
(3655, 3662, 0),
|
||||
(3761, 3761, 0),
|
||||
(3764, 3769, 0),
|
||||
(3771, 3772, 0),
|
||||
(3784, 3789, 0),
|
||||
(3864, 3865, 0),
|
||||
(3893, 3893, 0),
|
||||
(3895, 3895, 0),
|
||||
(3897, 3897, 0),
|
||||
(3953, 3966, 0),
|
||||
(3968, 3972, 0),
|
||||
(3974, 3975, 0),
|
||||
(3981, 3991, 0),
|
||||
(3993, 4028, 0),
|
||||
(4038, 4038, 0),
|
||||
(4141, 4144, 0),
|
||||
(4146, 4151, 0),
|
||||
(4153, 4154, 0),
|
||||
(4157, 4158, 0),
|
||||
(4184, 4185, 0),
|
||||
(4190, 4192, 0),
|
||||
(4209, 4212, 0),
|
||||
(4226, 4226, 0),
|
||||
(4229, 4230, 0),
|
||||
(4237, 4237, 0),
|
||||
(4253, 4253, 0),
|
||||
(4352, 4447, 2),
|
||||
(4957, 4959, 0),
|
||||
(5906, 5908, 0),
|
||||
(5938, 5940, 0),
|
||||
(5970, 5971, 0),
|
||||
(6002, 6003, 0),
|
||||
(6068, 6069, 0),
|
||||
(6071, 6077, 0),
|
||||
(6086, 6086, 0),
|
||||
(6089, 6099, 0),
|
||||
(6109, 6109, 0),
|
||||
(6155, 6157, 0),
|
||||
(6277, 6278, 0),
|
||||
(6313, 6313, 0),
|
||||
(6432, 6434, 0),
|
||||
(6439, 6440, 0),
|
||||
(6450, 6450, 0),
|
||||
(6457, 6459, 0),
|
||||
(6679, 6680, 0),
|
||||
(6683, 6683, 0),
|
||||
(6742, 6742, 0),
|
||||
(6744, 6750, 0),
|
||||
(6752, 6752, 0),
|
||||
(6754, 6754, 0),
|
||||
(6757, 6764, 0),
|
||||
(6771, 6780, 0),
|
||||
(6783, 6783, 0),
|
||||
(6832, 6846, 0),
|
||||
(6912, 6915, 0),
|
||||
(6964, 6964, 0),
|
||||
(6966, 6970, 0),
|
||||
(6972, 6972, 0),
|
||||
(6978, 6978, 0),
|
||||
(7019, 7027, 0),
|
||||
(7040, 7041, 0),
|
||||
(7074, 7077, 0),
|
||||
(7080, 7081, 0),
|
||||
(7083, 7085, 0),
|
||||
(7142, 7142, 0),
|
||||
(7144, 7145, 0),
|
||||
(7149, 7149, 0),
|
||||
(7151, 7153, 0),
|
||||
(7212, 7219, 0),
|
||||
(7222, 7223, 0),
|
||||
(7376, 7378, 0),
|
||||
(7380, 7392, 0),
|
||||
(7394, 7400, 0),
|
||||
(7405, 7405, 0),
|
||||
(7412, 7412, 0),
|
||||
(7416, 7417, 0),
|
||||
(7616, 7669, 0),
|
||||
(7675, 7679, 0),
|
||||
(8203, 8207, 0),
|
||||
(8232, 8238, 0),
|
||||
(8288, 8291, 0),
|
||||
(8400, 8432, 0),
|
||||
(8986, 8987, 2),
|
||||
(9001, 9002, 2),
|
||||
(9193, 9196, 2),
|
||||
(9200, 9200, 2),
|
||||
(9203, 9203, 2),
|
||||
(9725, 9726, 2),
|
||||
(9748, 9749, 2),
|
||||
(9800, 9811, 2),
|
||||
(9855, 9855, 2),
|
||||
(9875, 9875, 2),
|
||||
(9889, 9889, 2),
|
||||
(9898, 9899, 2),
|
||||
(9917, 9918, 2),
|
||||
(9924, 9925, 2),
|
||||
(9934, 9934, 2),
|
||||
(9940, 9940, 2),
|
||||
(9962, 9962, 2),
|
||||
(9970, 9971, 2),
|
||||
(9973, 9973, 2),
|
||||
(9978, 9978, 2),
|
||||
(9981, 9981, 2),
|
||||
(9989, 9989, 2),
|
||||
(9994, 9995, 2),
|
||||
(10024, 10024, 2),
|
||||
(10060, 10060, 2),
|
||||
(10062, 10062, 2),
|
||||
(10067, 10069, 2),
|
||||
(10071, 10071, 2),
|
||||
(10133, 10135, 2),
|
||||
(10160, 10160, 2),
|
||||
(10175, 10175, 2),
|
||||
(11035, 11036, 2),
|
||||
(11088, 11088, 2),
|
||||
(11093, 11093, 2),
|
||||
(11503, 11505, 0),
|
||||
(11647, 11647, 0),
|
||||
(11744, 11775, 0),
|
||||
(11904, 11929, 2),
|
||||
(11931, 12019, 2),
|
||||
(12032, 12245, 2),
|
||||
(12272, 12283, 2),
|
||||
(12288, 12329, 2),
|
||||
(12330, 12333, 0),
|
||||
(12334, 12350, 2),
|
||||
(12353, 12438, 2),
|
||||
(12441, 12442, 0),
|
||||
(12443, 12543, 2),
|
||||
(12549, 12591, 2),
|
||||
(12593, 12686, 2),
|
||||
(12688, 12730, 2),
|
||||
(12736, 12771, 2),
|
||||
(12784, 12830, 2),
|
||||
(12832, 12871, 2),
|
||||
(12880, 19903, 2),
|
||||
(19968, 42124, 2),
|
||||
(42128, 42182, 2),
|
||||
(42607, 42610, 0),
|
||||
(42612, 42621, 0),
|
||||
(42654, 42655, 0),
|
||||
(42736, 42737, 0),
|
||||
(43010, 43010, 0),
|
||||
(43014, 43014, 0),
|
||||
(43019, 43019, 0),
|
||||
(43045, 43046, 0),
|
||||
(43204, 43205, 0),
|
||||
(43232, 43249, 0),
|
||||
(43302, 43309, 0),
|
||||
(43335, 43345, 0),
|
||||
(43360, 43388, 2),
|
||||
(43392, 43394, 0),
|
||||
(43443, 43443, 0),
|
||||
(43446, 43449, 0),
|
||||
(43452, 43452, 0),
|
||||
(43493, 43493, 0),
|
||||
(43561, 43566, 0),
|
||||
(43569, 43570, 0),
|
||||
(43573, 43574, 0),
|
||||
(43587, 43587, 0),
|
||||
(43596, 43596, 0),
|
||||
(43644, 43644, 0),
|
||||
(43696, 43696, 0),
|
||||
(43698, 43700, 0),
|
||||
(43703, 43704, 0),
|
||||
(43710, 43711, 0),
|
||||
(43713, 43713, 0),
|
||||
(43756, 43757, 0),
|
||||
(43766, 43766, 0),
|
||||
(44005, 44005, 0),
|
||||
(44008, 44008, 0),
|
||||
(44013, 44013, 0),
|
||||
(44032, 55203, 2),
|
||||
(63744, 64255, 2),
|
||||
(64286, 64286, 0),
|
||||
(65024, 65039, 0),
|
||||
(65040, 65049, 2),
|
||||
(65056, 65071, 0),
|
||||
(65072, 65106, 2),
|
||||
(65108, 65126, 2),
|
||||
(65128, 65131, 2),
|
||||
(65281, 65376, 2),
|
||||
(65504, 65510, 2),
|
||||
(66045, 66045, 0),
|
||||
(66272, 66272, 0),
|
||||
(66422, 66426, 0),
|
||||
(68097, 68099, 0),
|
||||
(68101, 68102, 0),
|
||||
(68108, 68111, 0),
|
||||
(68152, 68154, 0),
|
||||
(68159, 68159, 0),
|
||||
(68325, 68326, 0),
|
||||
(69633, 69633, 0),
|
||||
(69688, 69702, 0),
|
||||
(69759, 69761, 0),
|
||||
(69811, 69814, 0),
|
||||
(69817, 69818, 0),
|
||||
(69888, 69890, 0),
|
||||
(69927, 69931, 0),
|
||||
(69933, 69940, 0),
|
||||
(70003, 70003, 0),
|
||||
(70016, 70017, 0),
|
||||
(70070, 70078, 0),
|
||||
(70090, 70092, 0),
|
||||
(70191, 70193, 0),
|
||||
(70196, 70196, 0),
|
||||
(70198, 70199, 0),
|
||||
(70206, 70206, 0),
|
||||
(70367, 70367, 0),
|
||||
(70371, 70378, 0),
|
||||
(70400, 70401, 0),
|
||||
(70460, 70460, 0),
|
||||
(70464, 70464, 0),
|
||||
(70502, 70508, 0),
|
||||
(70512, 70516, 0),
|
||||
(70712, 70719, 0),
|
||||
(70722, 70724, 0),
|
||||
(70726, 70726, 0),
|
||||
(70835, 70840, 0),
|
||||
(70842, 70842, 0),
|
||||
(70847, 70848, 0),
|
||||
(70850, 70851, 0),
|
||||
(71090, 71093, 0),
|
||||
(71100, 71101, 0),
|
||||
(71103, 71104, 0),
|
||||
(71132, 71133, 0),
|
||||
(71219, 71226, 0),
|
||||
(71229, 71229, 0),
|
||||
(71231, 71232, 0),
|
||||
(71339, 71339, 0),
|
||||
(71341, 71341, 0),
|
||||
(71344, 71349, 0),
|
||||
(71351, 71351, 0),
|
||||
(71453, 71455, 0),
|
||||
(71458, 71461, 0),
|
||||
(71463, 71467, 0),
|
||||
(72752, 72758, 0),
|
||||
(72760, 72765, 0),
|
||||
(72767, 72767, 0),
|
||||
(72850, 72871, 0),
|
||||
(72874, 72880, 0),
|
||||
(72882, 72883, 0),
|
||||
(72885, 72886, 0),
|
||||
(92912, 92916, 0),
|
||||
(92976, 92982, 0),
|
||||
(94095, 94098, 0),
|
||||
(94176, 94179, 2),
|
||||
(94208, 100343, 2),
|
||||
(100352, 101106, 2),
|
||||
(110592, 110878, 2),
|
||||
(110928, 110930, 2),
|
||||
(110948, 110951, 2),
|
||||
(110960, 111355, 2),
|
||||
(113821, 113822, 0),
|
||||
(119143, 119145, 0),
|
||||
(119163, 119170, 0),
|
||||
(119173, 119179, 0),
|
||||
(119210, 119213, 0),
|
||||
(119362, 119364, 0),
|
||||
(121344, 121398, 0),
|
||||
(121403, 121452, 0),
|
||||
(121461, 121461, 0),
|
||||
(121476, 121476, 0),
|
||||
(121499, 121503, 0),
|
||||
(121505, 121519, 0),
|
||||
(122880, 122886, 0),
|
||||
(122888, 122904, 0),
|
||||
(122907, 122913, 0),
|
||||
(122915, 122916, 0),
|
||||
(122918, 122922, 0),
|
||||
(125136, 125142, 0),
|
||||
(125252, 125258, 0),
|
||||
(126980, 126980, 2),
|
||||
(127183, 127183, 2),
|
||||
(127374, 127374, 2),
|
||||
(127377, 127386, 2),
|
||||
(127488, 127490, 2),
|
||||
(127504, 127547, 2),
|
||||
(127552, 127560, 2),
|
||||
(127568, 127569, 2),
|
||||
(127584, 127589, 2),
|
||||
(127744, 127776, 2),
|
||||
(127789, 127797, 2),
|
||||
(127799, 127868, 2),
|
||||
(127870, 127891, 2),
|
||||
(127904, 127946, 2),
|
||||
(127951, 127955, 2),
|
||||
(127968, 127984, 2),
|
||||
(127988, 127988, 2),
|
||||
(127992, 128062, 2),
|
||||
(128064, 128064, 2),
|
||||
(128066, 128252, 2),
|
||||
(128255, 128317, 2),
|
||||
(128331, 128334, 2),
|
||||
(128336, 128359, 2),
|
||||
(128378, 128378, 2),
|
||||
(128405, 128406, 2),
|
||||
(128420, 128420, 2),
|
||||
(128507, 128591, 2),
|
||||
(128640, 128709, 2),
|
||||
(128716, 128716, 2),
|
||||
(128720, 128722, 2),
|
||||
(128725, 128725, 2),
|
||||
(128747, 128748, 2),
|
||||
(128756, 128762, 2),
|
||||
(128992, 129003, 2),
|
||||
(129293, 129393, 2),
|
||||
(129395, 129398, 2),
|
||||
(129402, 129442, 2),
|
||||
(129445, 129450, 2),
|
||||
(129454, 129482, 2),
|
||||
(129485, 129535, 2),
|
||||
(129648, 129651, 2),
|
||||
(129656, 129658, 2),
|
||||
(129664, 129666, 2),
|
||||
(129680, 129685, 2),
|
||||
(131072, 196605, 2),
|
||||
(196608, 262141, 2),
|
||||
(917760, 917999, 0),
|
||||
]
|
34
rich/_lru_cache.py
Normal file
34
rich/_lru_cache.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
from collections import OrderedDict
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
|
||||
CacheKey = TypeVar("CacheKey")
|
||||
CacheValue = TypeVar("CacheValue")
|
||||
|
||||
|
||||
class LRUCache(Generic[CacheKey, CacheValue], OrderedDict):
|
||||
"""
|
||||
A dictionary-like container that stores a given maximum items.
|
||||
|
||||
If an additional item is added when the LRUCache is full, the least
|
||||
recently used key is discarded to make room for the new item.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, cache_size: int) -> None:
|
||||
self.cache_size = cache_size
|
||||
super(LRUCache, self).__init__()
|
||||
|
||||
def __setitem__(self, key: CacheKey, value: CacheValue) -> None:
|
||||
"""Store a new views, potentially discarding an old value."""
|
||||
if key not in self:
|
||||
if len(self) >= self.cache_size:
|
||||
self.popitem(last=False)
|
||||
OrderedDict.__setitem__(self, key, value)
|
||||
|
||||
def __getitem__(self, key: CacheKey) -> CacheValue:
|
||||
"""Gets the item, but also makes it most recent."""
|
||||
value: CacheValue = OrderedDict.__getitem__(self, key)
|
||||
OrderedDict.__delitem__(self, key)
|
||||
OrderedDict.__setitem__(self, key, value)
|
||||
return value
|
|
@ -1,6 +1,9 @@
|
|||
import re
|
||||
from typing import Iterable, List, Tuple
|
||||
|
||||
from .cells import cell_len, chop_cells
|
||||
from ._tools import iter_last
|
||||
|
||||
re_word = re.compile(r"\s*\S+\s*")
|
||||
|
||||
|
||||
|
@ -18,14 +21,18 @@ def divide_line(text: str, width: int) -> List[int]:
|
|||
divides: List[int] = []
|
||||
append = divides.append
|
||||
line_position = 0
|
||||
for start, end, word in words(text):
|
||||
if line_position + len(word.rstrip()) > width:
|
||||
for start, _end, word in words(text):
|
||||
if line_position + cell_len(word.rstrip()) > width:
|
||||
if line_position and start:
|
||||
append(start)
|
||||
line_position = len(word)
|
||||
line_position = cell_len(word)
|
||||
else:
|
||||
divides.extend(range(start or width, end + 1, width))
|
||||
line_position = len(word) % width
|
||||
for last, line in iter_last(chop_cells(text, width)):
|
||||
if last:
|
||||
line_position = cell_len(line)
|
||||
else:
|
||||
start += len(line)
|
||||
append(start)
|
||||
else:
|
||||
line_position += len(word)
|
||||
line_position += cell_len(word)
|
||||
return divides
|
||||
|
|
|
@ -79,7 +79,6 @@ class Bar:
|
|||
remaining_bars -= 1
|
||||
if remaining_bars:
|
||||
yield Segment(bar * remaining_bars, style)
|
||||
yield Segment("\r")
|
||||
|
||||
def __measure__(self, console: Console, max_width: int) -> Measurement:
|
||||
if self.width is not None:
|
||||
|
@ -97,6 +96,7 @@ if __name__ == "__main__":
|
|||
for n in range(0, 101, 1):
|
||||
bar.update_progress(n)
|
||||
console.print(bar)
|
||||
console.file.write("\r")
|
||||
time.sleep(0.05)
|
||||
console.show_cursor(True)
|
||||
console.print()
|
||||
|
|
113
rich/cells.py
Normal file
113
rich/cells.py
Normal file
|
@ -0,0 +1,113 @@
|
|||
from functools import lru_cache
|
||||
from itertools import takewhile
|
||||
from typing import List, Tuple
|
||||
|
||||
from ._cell_widths import CELL_WIDTHS
|
||||
from ._lru_cache import LRUCache
|
||||
|
||||
|
||||
def cell_len(text: str, _cache: LRUCache[str, int] = LRUCache(1024)) -> int:
|
||||
"""Get the number of cells required to display text.
|
||||
|
||||
Args:
|
||||
text (str): Text to display.
|
||||
|
||||
Returns:
|
||||
int: Number of cells required to display the text.
|
||||
"""
|
||||
cached_result = _cache.get(text, None)
|
||||
if cached_result is not None:
|
||||
return cached_result
|
||||
_get_size = get_character_cell_size
|
||||
total_size = sum(_get_size(character) for character in text)
|
||||
if len(text) < 256:
|
||||
_cache[text] = total_size
|
||||
return total_size
|
||||
|
||||
|
||||
@lru_cache(maxsize=5000)
|
||||
def get_character_cell_size(character: str) -> int:
|
||||
"""Get the cell size of a character.
|
||||
|
||||
Args:
|
||||
character (str): A single character.
|
||||
|
||||
Returns:
|
||||
int: Number of cells (0, 1 or 2) occupied by that character.
|
||||
"""
|
||||
|
||||
codepoint = ord(character)
|
||||
if 127 > codepoint > 31:
|
||||
# Shortcut for ascii
|
||||
return 1
|
||||
|
||||
_table = CELL_WIDTHS
|
||||
lower_bound = 0
|
||||
upper_bound = len(_table) - 1
|
||||
index = (lower_bound + upper_bound) // 2
|
||||
while True:
|
||||
start, end, width = _table[index]
|
||||
if codepoint < start:
|
||||
upper_bound = index - 1
|
||||
elif codepoint > end:
|
||||
lower_bound = index + 1
|
||||
else:
|
||||
return 0 if width == -1 else width
|
||||
if upper_bound < lower_bound:
|
||||
break
|
||||
index = (lower_bound + upper_bound) // 2
|
||||
return 1
|
||||
|
||||
|
||||
def set_cell_size(text: str, total: int) -> str:
|
||||
"""Set the length of a string to fit within given number of cells."""
|
||||
cell_size = cell_len(text)
|
||||
if cell_size == total:
|
||||
return text
|
||||
if cell_size < total:
|
||||
return text + " " * (total - cell_size)
|
||||
|
||||
_get_character_cell_size = get_character_cell_size
|
||||
character_sizes = [_get_character_cell_size(character) for character in text]
|
||||
excess = cell_size - total
|
||||
pop = character_sizes.pop
|
||||
while excess > 0:
|
||||
excess -= pop()
|
||||
text = text[: len(character_sizes)]
|
||||
if excess == -1:
|
||||
text += " "
|
||||
return text
|
||||
|
||||
|
||||
def chop_cells(text: str, max_size: int) -> List[str]:
|
||||
"""Break text in to equal (cell) length strings."""
|
||||
_get_character_cell_size = get_character_cell_size
|
||||
characters = [
|
||||
(character, _get_character_cell_size(character)) for character in text
|
||||
][::-1]
|
||||
total_size = 0
|
||||
lines: List[List[str]] = [[]]
|
||||
append = lines[-1].append
|
||||
|
||||
pop = characters.pop
|
||||
while characters:
|
||||
character, size = pop()
|
||||
if total_size + size > max_size:
|
||||
lines.append([character])
|
||||
append = lines[-1].append
|
||||
total_size = size
|
||||
else:
|
||||
total_size += size
|
||||
append(character)
|
||||
return ["".join(line) for line in lines]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
print(get_character_cell_size("😽"))
|
||||
for line in chop_cells("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", 8):
|
||||
print(line)
|
||||
for n in range(80, 1, -1):
|
||||
print(set_cell_size("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", n) + "|")
|
||||
print("x" * n)
|
||||
|
11
rich/cjk.py
Normal file
11
rich/cjk.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
|
||||
console = Console(width=16)
|
||||
|
||||
console.print(Panel("""这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑。""", expand=False))
|
||||
|
||||
console.print(Panel(""":pile_of_poo:""", expand=False))
|
||||
|
||||
console.print(Panel(""":pile_of_poo::vampire::thumbs_up: """ * 5))
|
||||
print("x" * 15)
|
|
@ -234,9 +234,10 @@ class Console:
|
|||
width (int, optional): The width of the terminal. Leave as default to auto-detect width.
|
||||
height (int, optional): The height of the terminal. Leave as default to auto-detect height.
|
||||
record (bool, optional): Boolean to enable recording of terminal output,
|
||||
required to call :meth:`export_html` and :meth:`export_text`. Defaults to False.
|
||||
emoji (Optional[bool], optional): Enable emoji code. Defaults to True.
|
||||
required to call :meth:`export_html` and :meth:`export_text`. Defaults to False.
|
||||
markup (bool, optional): Boolean to enable :ref:`console_markup`. Defaults to True.
|
||||
emoji (bool, optional): Enable emoji code. Defaults to True.
|
||||
highlight (bool, optional): Enable automatic highlighting. Defaults to True.
|
||||
log_time (bool, optional): Boolean to enable logging of time by :meth:`log` methods. Defaults to True.
|
||||
log_path (bool, optional): Boolean to enable the logging of the caller by :meth:`log`. Defaults to True.
|
||||
log_time_format (str, optional): Log time format if ``log_time`` is enabled. Defaults to "[%X] ".
|
||||
|
@ -256,6 +257,7 @@ class Console:
|
|||
record: bool = False,
|
||||
markup: bool = True,
|
||||
emoji: bool = True,
|
||||
highlight: bool = True,
|
||||
log_time: bool = True,
|
||||
log_path: bool = True,
|
||||
log_time_format: str = "[%X] ",
|
||||
|
@ -271,6 +273,7 @@ class Console:
|
|||
self.record = record
|
||||
self._markup = markup
|
||||
self._emoji = emoji
|
||||
self._highlight = highlight
|
||||
|
||||
if color_system is None:
|
||||
self._color_system = None
|
||||
|
@ -319,9 +322,7 @@ class Console:
|
|||
"""Detect color system from env vars."""
|
||||
if not self.is_terminal:
|
||||
return None
|
||||
if WINDOWS:
|
||||
return ColorSystem.WINDOWS
|
||||
if os.environ.get("COLORTERM", "").strip().lower() == "truecolor":
|
||||
if os.environ.get("COLORTERM", "").strip().lower() in ("truecolor", "24bit"):
|
||||
return ColorSystem.TRUECOLOR
|
||||
# 256 can be considered standard nowadays
|
||||
return ColorSystem.EIGHT_BIT
|
||||
|
@ -410,8 +411,6 @@ class Console:
|
|||
return ConsoleDimensions(self._width, self._height)
|
||||
|
||||
width, height = shutil.get_terminal_size()
|
||||
# Fixes Issue with Windows console (https://github.com/willmcgugan/rich/issues/7)
|
||||
width -= 1
|
||||
return ConsoleDimensions(
|
||||
width if self._width is None else self._width,
|
||||
height if self._height is None else self._height,
|
||||
|
@ -445,10 +444,8 @@ class Console:
|
|||
Args:
|
||||
show (bool, optional): Set visibility of the cursor.
|
||||
"""
|
||||
if WINDOWS:
|
||||
return
|
||||
self._buffer.append(Segment("\033[?25h" if show else "\033[?25l"))
|
||||
self._check_buffer()
|
||||
self.file.write("\033[?25h" if show else "\033[?25l")
|
||||
|
||||
def _render(
|
||||
self,
|
||||
|
@ -642,17 +639,17 @@ class Console:
|
|||
end: str,
|
||||
emoji: bool = None,
|
||||
markup: bool = None,
|
||||
highlight: bool = True,
|
||||
highlight: bool = None,
|
||||
) -> List[ConsoleRenderable]:
|
||||
"""Combined a number of renderables and text in to one renderable.
|
||||
|
||||
Args:
|
||||
renderables (Iterable[Union[str, ConsoleRenderable]]): [description]
|
||||
renderables (Iterable[Union[str, ConsoleRenderable]]): Anyting that Rich can render.
|
||||
sep (str, optional): String to write between print data. Defaults to " ".
|
||||
end (str, optional): String to write at end of print data. Defaults to "\n".
|
||||
emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default.
|
||||
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default.
|
||||
highlight (bool, optional): Perform highlighting. Defaults to True.
|
||||
highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default.
|
||||
|
||||
Returns:
|
||||
List[ConsoleRenderable]: A list of things to render.
|
||||
|
@ -664,7 +661,7 @@ class Console:
|
|||
append_text = text.append
|
||||
|
||||
_highlighter: HighlighterType
|
||||
if highlight:
|
||||
if highlight or (highlight is None and self._highlight):
|
||||
_highlighter = self.highlighter
|
||||
else:
|
||||
_highlighter = _null_highlighter
|
||||
|
@ -723,7 +720,7 @@ class Console:
|
|||
style: Union[str, Style] = None,
|
||||
emoji: bool = None,
|
||||
markup: bool = None,
|
||||
highlight: bool = True,
|
||||
highlight: bool = None,
|
||||
) -> None:
|
||||
r"""Print to the console.
|
||||
|
||||
|
@ -732,9 +729,9 @@ class Console:
|
|||
sep (str, optional): String to write between print data. Defaults to " ".
|
||||
end (str, optional): String to write at end of print data. Defaults to "\n".
|
||||
style (Union[str, Style], optional): A style to apply to output. Defaults to None.
|
||||
emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default.
|
||||
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default.
|
||||
highlight (bool, optional): Perform highlighting. Defaults to True.
|
||||
emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None.
|
||||
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None
|
||||
highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None.
|
||||
"""
|
||||
if not objects:
|
||||
self.line()
|
||||
|
@ -783,7 +780,7 @@ class Console:
|
|||
end="\n",
|
||||
emoji: bool = None,
|
||||
markup: bool = None,
|
||||
highlight: bool = True,
|
||||
highlight: bool = None,
|
||||
log_locals: bool = False,
|
||||
_stack_offset=1,
|
||||
) -> None:
|
||||
|
@ -793,9 +790,9 @@ class Console:
|
|||
objects (positional args): Objects to log to the terminal.
|
||||
sep (str, optional): String to write between print data. Defaults to " ".
|
||||
end (str, optional): String to write at end of print data. Defaults to "\n".
|
||||
emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default.
|
||||
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default.
|
||||
highlight (bool, optional): Perform highlighting. Defaults to True.
|
||||
emoji (Optional[bool], optional): Enable emoji code, or ``None`` to use console default. Defaults to None.
|
||||
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None.
|
||||
highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None.
|
||||
log_locals (bool, optional): Boolean to enable logging of locals where ``log()``
|
||||
was called. Defaults to False.
|
||||
_stack_offset (int, optional): Offset of caller from end of call stack. Defaults to 1.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from collections import deque
|
||||
from collections.abc import Sized
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass, replace, field
|
||||
from datetime import timedelta
|
||||
|
@ -37,25 +38,42 @@ ProgressType = TypeVar("ProgressType")
|
|||
|
||||
|
||||
def track(
|
||||
sequence: Sequence[ProgressType], description="Working..."
|
||||
sequence: Union[Sequence[ProgressType], Iterable[ProgressType]],
|
||||
description="Working...",
|
||||
total: int = None,
|
||||
auto_refresh=True,
|
||||
) -> Iterable[ProgressType]:
|
||||
"""Track progress of processing a sequence.
|
||||
|
||||
Args:
|
||||
sequence (Sequence[ProgressType]): A sequence (must support "len") you wish to iterate over.
|
||||
description (str, optional): [description]. Defaults to "Working".
|
||||
sequence (Iterable[ProgressType]): A sequence (must support "len") you wish to iterate over.
|
||||
description (str, optional): Description of task show next to progress bar. Defaults to "Working".
|
||||
total: (int, optional): Total number of steps. Default is len(sequence).
|
||||
auto_refresh (bool, optional): Automatic refresh, disable to force a refresh after each iteration. Default is True.
|
||||
|
||||
Returns:
|
||||
Iterable[ProgressType]: An iterable of the values in the sequence.
|
||||
|
||||
"""
|
||||
progress = Progress(auto_refresh=False)
|
||||
task_id = progress.add_task(description, total=len(sequence))
|
||||
progress = Progress(auto_refresh=auto_refresh)
|
||||
|
||||
if total is None:
|
||||
if isinstance(sequence, Sized):
|
||||
task_total = len(sequence)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"unable to get size of {sequence!r}, please specify 'total'"
|
||||
)
|
||||
else:
|
||||
task_total = total
|
||||
|
||||
task_id = progress.add_task(description, total=task_total)
|
||||
with progress:
|
||||
for completed, value in enumerate(sequence, 1):
|
||||
yield value
|
||||
progress.update(task_id, completed=completed)
|
||||
progress.refresh()
|
||||
if not auto_refresh:
|
||||
progress.refresh()
|
||||
|
||||
|
||||
class ProgressColumn(ABC):
|
||||
|
@ -117,7 +135,7 @@ class TimeRemainingColumn(ProgressColumn):
|
|||
"""Show time remaining."""
|
||||
remaining = task.time_remaining
|
||||
if remaining is None:
|
||||
return Text("?", style="progress.remaining")
|
||||
return Text("-:--:--", style="progress.remaining")
|
||||
remaining_delta = timedelta(seconds=int(remaining))
|
||||
return Text(str(remaining_delta), style="progress.remaining")
|
||||
|
||||
|
@ -278,6 +296,7 @@ class Progress:
|
|||
self._lock = RLock()
|
||||
self._refresh_thread: Optional[_RefreshThread] = None
|
||||
self._refresh_count = 0
|
||||
self._enter_count = 0
|
||||
|
||||
@property
|
||||
def tasks_ids(self) -> List[TaskID]:
|
||||
|
@ -313,29 +332,56 @@ class Progress:
|
|||
self.console.show_cursor(True)
|
||||
|
||||
def __enter__(self) -> "Progress":
|
||||
self.start()
|
||||
return self
|
||||
with self._lock:
|
||||
if self._enter_count:
|
||||
self._enter_count += 1
|
||||
return self
|
||||
self.start()
|
||||
self._enter_count += 1
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||
self.stop()
|
||||
with self._lock:
|
||||
self._enter_count -= 1
|
||||
if not self._enter_count:
|
||||
self.stop()
|
||||
|
||||
def track(
|
||||
self, sequence: Sequence[ProgressType], description="Working..."
|
||||
self,
|
||||
sequence: Sequence[ProgressType],
|
||||
total: int = None,
|
||||
task_id: Optional[TaskID] = None,
|
||||
description="Working...",
|
||||
) -> Iterable[ProgressType]:
|
||||
"""[summary]
|
||||
|
||||
Args:
|
||||
sequence (Sequence[ProgressType]): [description]
|
||||
total: (int, optional): Total number of steps. Default is len(sequence).
|
||||
task_id: (TaskID): Task to track. Default is new task.
|
||||
description: (str, optional): Description of task, if new task is created.
|
||||
|
||||
Returns:
|
||||
Iterable[ProgressType]: [description]
|
||||
"""
|
||||
task_id = self.add_task(description, total=len(sequence))
|
||||
if total is None:
|
||||
if isinstance(sequence, Sized):
|
||||
task_total = len(sequence)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"unable to get size of {sequence!r}, please specify 'total'"
|
||||
)
|
||||
else:
|
||||
task_total = total
|
||||
|
||||
if task_id is None:
|
||||
task_id = self.add_task(description, total=task_total)
|
||||
else:
|
||||
self.update(task_id, total=task_total)
|
||||
with self:
|
||||
for completed, value in enumerate(sequence, 1):
|
||||
yield value
|
||||
self.update(task_id, completed=completed)
|
||||
self.refresh()
|
||||
|
||||
def start_task(self, task_id: TaskID) -> None:
|
||||
"""Start a task.
|
||||
|
@ -412,6 +458,15 @@ class Progress:
|
|||
if refresh:
|
||||
self.refresh()
|
||||
|
||||
def advance(self, task_id: TaskID, advance: float = 1) -> None:
|
||||
"""Advance task by a number of steps.
|
||||
|
||||
Args:
|
||||
task_id (TaskID): ID of task.
|
||||
advance (float): Number of steps to advance. Default is 1.
|
||||
"""
|
||||
self.update(task_id, advance=advance)
|
||||
|
||||
def refresh(self) -> None:
|
||||
"""Refresh (render) the progress information."""
|
||||
with self._lock:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from typing import NamedTuple, Optional
|
||||
|
||||
from .cells import cell_len, set_cell_size
|
||||
from .style import Style
|
||||
|
||||
from itertools import zip_longest
|
||||
|
@ -104,7 +105,7 @@ class Segment(NamedTuple):
|
|||
Returns:
|
||||
List[Segment]: A line of segments with the desired length.
|
||||
"""
|
||||
line_length = sum(len(text) for text, _style in line)
|
||||
line_length = sum(cell_len(text) for text, _style in line)
|
||||
new_line: List[Segment]
|
||||
|
||||
if line_length < length:
|
||||
|
@ -117,13 +118,14 @@ class Segment(NamedTuple):
|
|||
append = new_line.append
|
||||
line_length = 0
|
||||
for segment in line:
|
||||
segment_length = len(segment.text)
|
||||
segment_length = cell_len(segment.text)
|
||||
if line_length + segment_length < length:
|
||||
append(segment)
|
||||
line_length += segment_length
|
||||
else:
|
||||
text, style = segment
|
||||
append(cls(text[: length - line_length], style))
|
||||
text = set_cell_size(text, length - line_length)
|
||||
append(cls(text, style))
|
||||
break
|
||||
else:
|
||||
new_line = line[:]
|
||||
|
@ -139,7 +141,7 @@ class Segment(NamedTuple):
|
|||
Returns:
|
||||
int: The length of the line.
|
||||
"""
|
||||
return sum(len(text) for text, _ in line)
|
||||
return sum(cell_len(text) for text, _ in line)
|
||||
|
||||
@classmethod
|
||||
def get_shape(cls, lines: List[List["Segment"]]) -> Tuple[int, int]:
|
||||
|
|
|
@ -73,6 +73,7 @@ class Syntax:
|
|||
def from_path(
|
||||
cls,
|
||||
path: str,
|
||||
encoding: str = "utf-8",
|
||||
theme: Union[str, PygmentsStyle] = DEFAULT_THEME,
|
||||
dedent: bool = True,
|
||||
line_numbers: bool = False,
|
||||
|
@ -86,6 +87,7 @@ class Syntax:
|
|||
|
||||
Args:
|
||||
path (str): Path to file to highlight.
|
||||
encoding (str): Encoding of file.
|
||||
lexer_name (str): Lexer to use (see https://pygments.org/docs/lexers/)
|
||||
theme (str, optional): Color theme, aka Pygments style (see https://pygments.org/docs/styles/#getting-a-list-of-available-styles). Defaults to "emacs".
|
||||
dedent (bool, optional): Enable stripping of initial whitespace. Defaults to True.
|
||||
|
@ -99,7 +101,7 @@ class Syntax:
|
|||
Returns:
|
||||
[Syntax]: A Syntax object that may be printed to the console
|
||||
"""
|
||||
with open(path, "rt") as code_file:
|
||||
with open(path, "rt", encoding=encoding) as code_file:
|
||||
code = code_file.read()
|
||||
try:
|
||||
lexer = guess_lexer_for_filename(path, code)
|
||||
|
@ -242,7 +244,7 @@ class Syntax:
|
|||
padding = _Segment(" " * numbers_column_width, background_style)
|
||||
new_line = _Segment("\n")
|
||||
|
||||
line_pointer = "->" if WINDOWS else "❱ "
|
||||
line_pointer = "❱ "
|
||||
|
||||
for line_no, line in enumerate(lines, self.start_line + line_offset):
|
||||
wrapped_lines = console.render_lines(
|
||||
|
|
|
@ -461,8 +461,8 @@ class Table:
|
|||
if __name__ == "__main__":
|
||||
from .console import Console
|
||||
|
||||
c = Console(width=80)
|
||||
table = Table()
|
||||
c = Console()
|
||||
table = Table(expand=True)
|
||||
table.add_column(no_wrap=True)
|
||||
table.add_column()
|
||||
table.add_row(
|
||||
|
|
|
@ -23,6 +23,7 @@ if TYPE_CHECKING: # pragma: no cover
|
|||
RenderableType,
|
||||
)
|
||||
|
||||
from .cells import cell_len
|
||||
from .containers import Lines
|
||||
from .style import Style
|
||||
from .segment import Segment
|
||||
|
@ -322,9 +323,9 @@ class Text:
|
|||
def __measure__(self, console: "Console", max_width: int) -> Measurement:
|
||||
text = self.text
|
||||
if not text.strip():
|
||||
return Measurement(len(text), len(text))
|
||||
max_text_width = max(len(line) for line in text.splitlines())
|
||||
min_text_width = max(len(word) for word in text.split())
|
||||
return Measurement(cell_len(text), cell_len(text))
|
||||
max_text_width = max(cell_len(line) for line in text.splitlines())
|
||||
min_text_width = max(cell_len(word) for word in text.split())
|
||||
return Measurement(min_text_width, max_text_width)
|
||||
|
||||
def _render_line(
|
||||
|
|
|
@ -281,7 +281,7 @@ if __name__ == "__main__": # pragma: no cover
|
|||
console = Console()
|
||||
import sys
|
||||
|
||||
def bar(a):
|
||||
def bar(a): # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑
|
||||
print(1 / a)
|
||||
|
||||
def foo(a):
|
||||
|
|
91
tools/make_terminal_widths.py
Normal file
91
tools/make_terminal_widths.py
Normal file
|
@ -0,0 +1,91 @@
|
|||
import subprocess
|
||||
from typing import List, Tuple
|
||||
import sys
|
||||
|
||||
from rich.progress import Progress
|
||||
|
||||
from wcwidth import wcwidth
|
||||
|
||||
|
||||
progress = Progress()
|
||||
|
||||
|
||||
def make_widths_table():
|
||||
table: List[Tuple[int, int, int]] = []
|
||||
append = table.append
|
||||
|
||||
make_table_task = progress.add_task("Calculating table...")
|
||||
|
||||
widths = (
|
||||
(codepoint, wcwidth(chr(codepoint)))
|
||||
for codepoint in range(0, sys.maxunicode + 1)
|
||||
)
|
||||
|
||||
widths = [(codepoint, width) for codepoint, width in widths if width != 1]
|
||||
iter_widths = iter(widths)
|
||||
|
||||
endpoint, group_cell_size = next(iter_widths)
|
||||
start_codepoint = end_codepoint = endpoint
|
||||
for codepoint, cell_size in progress.track(
|
||||
iter_widths, task_id=make_table_task, total=len(widths) - 1
|
||||
):
|
||||
if cell_size != group_cell_size or codepoint != end_codepoint + 1:
|
||||
append((start_codepoint, end_codepoint, group_cell_size))
|
||||
start_codepoint = end_codepoint = codepoint
|
||||
group_cell_size = cell_size
|
||||
else:
|
||||
end_codepoint = codepoint
|
||||
append((start_codepoint, end_codepoint, group_cell_size))
|
||||
return table
|
||||
|
||||
|
||||
def get_cell_size(table: List[Tuple[int, int, int]], character: str) -> int:
|
||||
|
||||
codepoint = ord(character)
|
||||
lower_bound = 0
|
||||
upper_bound = len(table) - 1
|
||||
index = (lower_bound + upper_bound) // 2
|
||||
while True:
|
||||
start, end, width = table[index]
|
||||
if codepoint < start:
|
||||
upper_bound = index - 1
|
||||
elif codepoint > end:
|
||||
lower_bound = index + 1
|
||||
else:
|
||||
return width
|
||||
if upper_bound < lower_bound:
|
||||
break
|
||||
index = (lower_bound + upper_bound) // 2
|
||||
return 1
|
||||
|
||||
|
||||
def test(widths_table):
|
||||
for codepoint in progress.track(
|
||||
range(0, sys.maxunicode + 1), description="Testing..."
|
||||
):
|
||||
character = chr(codepoint)
|
||||
width1 = get_cell_size(widths_table, character)
|
||||
width2 = wcwidth(character)
|
||||
if width1 != width2:
|
||||
print(f"{width1} != {width2}")
|
||||
break
|
||||
|
||||
|
||||
def run():
|
||||
with progress:
|
||||
widths_table = make_widths_table()
|
||||
test(widths_table)
|
||||
table_file = f"""# Auto generated by make_terminal_widths.py
|
||||
|
||||
CELL_WIDTHS = {widths_table!r}
|
||||
|
||||
"""
|
||||
with open("../rich/_cell_widths.py", "wt") as fh:
|
||||
fh.write(table_file)
|
||||
|
||||
subprocess.run("black ../rich/_cell_widths.py", shell=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue