mirror of
https://github.com/Textualize/rich.git
synced 2025-08-26 05:04:15 +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
|
mypy_report
|
||||||
docs/build
|
docs/build
|
||||||
docs/source/_build
|
docs/source/_build
|
||||||
|
tools/*.txt
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__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/),
|
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).
|
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
|
## [0.7.2] - 2020-03-15
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
name = "rich"
|
name = "rich"
|
||||||
homepage = "https://github.com/willmcgugan/rich"
|
homepage = "https://github.com/willmcgugan/rich"
|
||||||
documentation = "https://rich.readthedocs.io/en/latest/"
|
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"
|
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
|
||||||
authors = ["Will McGugan <willmcgugan@gmail.com>"]
|
authors = ["Will McGugan <willmcgugan@gmail.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
@ -25,7 +25,6 @@ python = "^3.6"
|
||||||
pprintpp = "^0.4.0"
|
pprintpp = "^0.4.0"
|
||||||
typing-extensions = "^3.7.4"
|
typing-extensions = "^3.7.4"
|
||||||
dataclasses = {version="^0.7", python = "~3.6"}
|
dataclasses = {version="^0.7", python = "~3.6"}
|
||||||
colorama = "^0.4.3"
|
|
||||||
pygments = "^2.5.0"
|
pygments = "^2.5.0"
|
||||||
commonmark = "^0.9.0"
|
commonmark = "^0.9.0"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from colorama import init
|
|
||||||
|
|
||||||
from typing import Any, IO, Optional, TYPE_CHECKING
|
from typing import Any, IO, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -22,7 +20,5 @@ def print(
|
||||||
return write_console.print(*objects, sep=sep, end=end)
|
return write_console.print(*objects, sep=sep, end=end)
|
||||||
|
|
||||||
|
|
||||||
init()
|
|
||||||
|
|
||||||
if __name__ == "__main__": # pragma: no cover
|
if __name__ == "__main__": # pragma: no cover
|
||||||
print("Hello, **World**")
|
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
|
import re
|
||||||
from typing import Iterable, List, Tuple
|
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*")
|
re_word = re.compile(r"\s*\S+\s*")
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,14 +21,18 @@ def divide_line(text: str, width: int) -> List[int]:
|
||||||
divides: List[int] = []
|
divides: List[int] = []
|
||||||
append = divides.append
|
append = divides.append
|
||||||
line_position = 0
|
line_position = 0
|
||||||
for start, end, word in words(text):
|
for start, _end, word in words(text):
|
||||||
if line_position + len(word.rstrip()) > width:
|
if line_position + cell_len(word.rstrip()) > width:
|
||||||
if line_position and start:
|
if line_position and start:
|
||||||
append(start)
|
append(start)
|
||||||
line_position = len(word)
|
line_position = cell_len(word)
|
||||||
else:
|
else:
|
||||||
divides.extend(range(start or width, end + 1, width))
|
for last, line in iter_last(chop_cells(text, width)):
|
||||||
line_position = len(word) % width
|
if last:
|
||||||
|
line_position = cell_len(line)
|
||||||
else:
|
else:
|
||||||
line_position += len(word)
|
start += len(line)
|
||||||
|
append(start)
|
||||||
|
else:
|
||||||
|
line_position += cell_len(word)
|
||||||
return divides
|
return divides
|
||||||
|
|
|
@ -79,7 +79,6 @@ class Bar:
|
||||||
remaining_bars -= 1
|
remaining_bars -= 1
|
||||||
if remaining_bars:
|
if remaining_bars:
|
||||||
yield Segment(bar * remaining_bars, style)
|
yield Segment(bar * remaining_bars, style)
|
||||||
yield Segment("\r")
|
|
||||||
|
|
||||||
def __measure__(self, console: Console, max_width: int) -> Measurement:
|
def __measure__(self, console: Console, max_width: int) -> Measurement:
|
||||||
if self.width is not None:
|
if self.width is not None:
|
||||||
|
@ -97,6 +96,7 @@ if __name__ == "__main__":
|
||||||
for n in range(0, 101, 1):
|
for n in range(0, 101, 1):
|
||||||
bar.update_progress(n)
|
bar.update_progress(n)
|
||||||
console.print(bar)
|
console.print(bar)
|
||||||
|
console.file.write("\r")
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
console.show_cursor(True)
|
console.show_cursor(True)
|
||||||
console.print()
|
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)
|
|
@ -235,8 +235,9 @@ class Console:
|
||||||
height (int, optional): The height of the terminal. Leave as default to auto-detect height.
|
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,
|
record (bool, optional): Boolean to enable recording of terminal output,
|
||||||
required to call :meth:`export_html` and :meth:`export_text`. Defaults to False.
|
required to call :meth:`export_html` and :meth:`export_text`. Defaults to False.
|
||||||
emoji (Optional[bool], optional): Enable emoji code. Defaults to True.
|
|
||||||
markup (bool, optional): Boolean to enable :ref:`console_markup`. Defaults to True.
|
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_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_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] ".
|
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,
|
record: bool = False,
|
||||||
markup: bool = True,
|
markup: bool = True,
|
||||||
emoji: bool = True,
|
emoji: bool = True,
|
||||||
|
highlight: bool = True,
|
||||||
log_time: bool = True,
|
log_time: bool = True,
|
||||||
log_path: bool = True,
|
log_path: bool = True,
|
||||||
log_time_format: str = "[%X] ",
|
log_time_format: str = "[%X] ",
|
||||||
|
@ -271,6 +273,7 @@ class Console:
|
||||||
self.record = record
|
self.record = record
|
||||||
self._markup = markup
|
self._markup = markup
|
||||||
self._emoji = emoji
|
self._emoji = emoji
|
||||||
|
self._highlight = highlight
|
||||||
|
|
||||||
if color_system is None:
|
if color_system is None:
|
||||||
self._color_system = None
|
self._color_system = None
|
||||||
|
@ -319,9 +322,7 @@ class Console:
|
||||||
"""Detect color system from env vars."""
|
"""Detect color system from env vars."""
|
||||||
if not self.is_terminal:
|
if not self.is_terminal:
|
||||||
return None
|
return None
|
||||||
if WINDOWS:
|
if os.environ.get("COLORTERM", "").strip().lower() in ("truecolor", "24bit"):
|
||||||
return ColorSystem.WINDOWS
|
|
||||||
if os.environ.get("COLORTERM", "").strip().lower() == "truecolor":
|
|
||||||
return ColorSystem.TRUECOLOR
|
return ColorSystem.TRUECOLOR
|
||||||
# 256 can be considered standard nowadays
|
# 256 can be considered standard nowadays
|
||||||
return ColorSystem.EIGHT_BIT
|
return ColorSystem.EIGHT_BIT
|
||||||
|
@ -410,8 +411,6 @@ class Console:
|
||||||
return ConsoleDimensions(self._width, self._height)
|
return ConsoleDimensions(self._width, self._height)
|
||||||
|
|
||||||
width, height = shutil.get_terminal_size()
|
width, height = shutil.get_terminal_size()
|
||||||
# Fixes Issue with Windows console (https://github.com/willmcgugan/rich/issues/7)
|
|
||||||
width -= 1
|
|
||||||
return ConsoleDimensions(
|
return ConsoleDimensions(
|
||||||
width if self._width is None else self._width,
|
width if self._width is None else self._width,
|
||||||
height if self._height is None else self._height,
|
height if self._height is None else self._height,
|
||||||
|
@ -445,10 +444,8 @@ class Console:
|
||||||
Args:
|
Args:
|
||||||
show (bool, optional): Set visibility of the cursor.
|
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._check_buffer()
|
||||||
|
self.file.write("\033[?25h" if show else "\033[?25l")
|
||||||
|
|
||||||
def _render(
|
def _render(
|
||||||
self,
|
self,
|
||||||
|
@ -642,17 +639,17 @@ class Console:
|
||||||
end: str,
|
end: str,
|
||||||
emoji: bool = None,
|
emoji: bool = None,
|
||||||
markup: bool = None,
|
markup: bool = None,
|
||||||
highlight: bool = True,
|
highlight: bool = None,
|
||||||
) -> List[ConsoleRenderable]:
|
) -> List[ConsoleRenderable]:
|
||||||
"""Combined a number of renderables and text in to one renderable.
|
"""Combined a number of renderables and text in to one renderable.
|
||||||
|
|
||||||
Args:
|
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 " ".
|
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".
|
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.
|
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.
|
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:
|
Returns:
|
||||||
List[ConsoleRenderable]: A list of things to render.
|
List[ConsoleRenderable]: A list of things to render.
|
||||||
|
@ -664,7 +661,7 @@ class Console:
|
||||||
append_text = text.append
|
append_text = text.append
|
||||||
|
|
||||||
_highlighter: HighlighterType
|
_highlighter: HighlighterType
|
||||||
if highlight:
|
if highlight or (highlight is None and self._highlight):
|
||||||
_highlighter = self.highlighter
|
_highlighter = self.highlighter
|
||||||
else:
|
else:
|
||||||
_highlighter = _null_highlighter
|
_highlighter = _null_highlighter
|
||||||
|
@ -723,7 +720,7 @@ class Console:
|
||||||
style: Union[str, Style] = None,
|
style: Union[str, Style] = None,
|
||||||
emoji: bool = None,
|
emoji: bool = None,
|
||||||
markup: bool = None,
|
markup: bool = None,
|
||||||
highlight: bool = True,
|
highlight: bool = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
r"""Print to the console.
|
r"""Print to the console.
|
||||||
|
|
||||||
|
@ -732,9 +729,9 @@ class Console:
|
||||||
sep (str, optional): String to write between print data. Defaults to " ".
|
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".
|
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.
|
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.
|
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.
|
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None
|
||||||
highlight (bool, optional): Perform highlighting. Defaults to True.
|
highlight (Optional[bool], optional): Enable automatic highlighting, or ``None`` to use console default. Defaults to None.
|
||||||
"""
|
"""
|
||||||
if not objects:
|
if not objects:
|
||||||
self.line()
|
self.line()
|
||||||
|
@ -783,7 +780,7 @@ class Console:
|
||||||
end="\n",
|
end="\n",
|
||||||
emoji: bool = None,
|
emoji: bool = None,
|
||||||
markup: bool = None,
|
markup: bool = None,
|
||||||
highlight: bool = True,
|
highlight: bool = None,
|
||||||
log_locals: bool = False,
|
log_locals: bool = False,
|
||||||
_stack_offset=1,
|
_stack_offset=1,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -793,9 +790,9 @@ class Console:
|
||||||
objects (positional args): Objects to log to the terminal.
|
objects (positional args): Objects to log to the terminal.
|
||||||
sep (str, optional): String to write between print data. Defaults to " ".
|
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".
|
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.
|
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.
|
markup (Optional[bool], optional): Enable markup, or ``None`` to use console default. Defaults to None.
|
||||||
highlight (bool, optional): Perform highlighting. Defaults to True.
|
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()``
|
log_locals (bool, optional): Boolean to enable logging of locals where ``log()``
|
||||||
was called. Defaults to False.
|
was called. Defaults to False.
|
||||||
_stack_offset (int, optional): Offset of caller from end of call stack. Defaults to 1.
|
_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 abc import ABC, abstractmethod
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from collections.abc import Sized
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from dataclasses import dataclass, replace, field
|
from dataclasses import dataclass, replace, field
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -37,24 +38,41 @@ ProgressType = TypeVar("ProgressType")
|
||||||
|
|
||||||
|
|
||||||
def track(
|
def track(
|
||||||
sequence: Sequence[ProgressType], description="Working..."
|
sequence: Union[Sequence[ProgressType], Iterable[ProgressType]],
|
||||||
|
description="Working...",
|
||||||
|
total: int = None,
|
||||||
|
auto_refresh=True,
|
||||||
) -> Iterable[ProgressType]:
|
) -> Iterable[ProgressType]:
|
||||||
"""Track progress of processing a sequence.
|
"""Track progress of processing a sequence.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sequence (Sequence[ProgressType]): A sequence (must support "len") you wish to iterate over.
|
sequence (Iterable[ProgressType]): A sequence (must support "len") you wish to iterate over.
|
||||||
description (str, optional): [description]. Defaults to "Working".
|
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:
|
Returns:
|
||||||
Iterable[ProgressType]: An iterable of the values in the sequence.
|
Iterable[ProgressType]: An iterable of the values in the sequence.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
progress = Progress(auto_refresh=False)
|
progress = Progress(auto_refresh=auto_refresh)
|
||||||
task_id = progress.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
|
||||||
|
|
||||||
|
task_id = progress.add_task(description, total=task_total)
|
||||||
with progress:
|
with progress:
|
||||||
for completed, value in enumerate(sequence, 1):
|
for completed, value in enumerate(sequence, 1):
|
||||||
yield value
|
yield value
|
||||||
progress.update(task_id, completed=completed)
|
progress.update(task_id, completed=completed)
|
||||||
|
if not auto_refresh:
|
||||||
progress.refresh()
|
progress.refresh()
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,7 +135,7 @@ class TimeRemainingColumn(ProgressColumn):
|
||||||
"""Show time remaining."""
|
"""Show time remaining."""
|
||||||
remaining = task.time_remaining
|
remaining = task.time_remaining
|
||||||
if remaining is None:
|
if remaining is None:
|
||||||
return Text("?", style="progress.remaining")
|
return Text("-:--:--", style="progress.remaining")
|
||||||
remaining_delta = timedelta(seconds=int(remaining))
|
remaining_delta = timedelta(seconds=int(remaining))
|
||||||
return Text(str(remaining_delta), style="progress.remaining")
|
return Text(str(remaining_delta), style="progress.remaining")
|
||||||
|
|
||||||
|
@ -278,6 +296,7 @@ class Progress:
|
||||||
self._lock = RLock()
|
self._lock = RLock()
|
||||||
self._refresh_thread: Optional[_RefreshThread] = None
|
self._refresh_thread: Optional[_RefreshThread] = None
|
||||||
self._refresh_count = 0
|
self._refresh_count = 0
|
||||||
|
self._enter_count = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tasks_ids(self) -> List[TaskID]:
|
def tasks_ids(self) -> List[TaskID]:
|
||||||
|
@ -313,29 +332,56 @@ class Progress:
|
||||||
self.console.show_cursor(True)
|
self.console.show_cursor(True)
|
||||||
|
|
||||||
def __enter__(self) -> "Progress":
|
def __enter__(self) -> "Progress":
|
||||||
|
with self._lock:
|
||||||
|
if self._enter_count:
|
||||||
|
self._enter_count += 1
|
||||||
|
return self
|
||||||
self.start()
|
self.start()
|
||||||
|
self._enter_count += 1
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||||
|
with self._lock:
|
||||||
|
self._enter_count -= 1
|
||||||
|
if not self._enter_count:
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
def track(
|
def track(
|
||||||
self, sequence: Sequence[ProgressType], description="Working..."
|
self,
|
||||||
|
sequence: Sequence[ProgressType],
|
||||||
|
total: int = None,
|
||||||
|
task_id: Optional[TaskID] = None,
|
||||||
|
description="Working...",
|
||||||
) -> Iterable[ProgressType]:
|
) -> Iterable[ProgressType]:
|
||||||
"""[summary]
|
"""[summary]
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sequence (Sequence[ProgressType]): [description]
|
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:
|
Returns:
|
||||||
Iterable[ProgressType]: [description]
|
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:
|
with self:
|
||||||
for completed, value in enumerate(sequence, 1):
|
for completed, value in enumerate(sequence, 1):
|
||||||
yield value
|
yield value
|
||||||
self.update(task_id, completed=completed)
|
self.update(task_id, completed=completed)
|
||||||
self.refresh()
|
|
||||||
|
|
||||||
def start_task(self, task_id: TaskID) -> None:
|
def start_task(self, task_id: TaskID) -> None:
|
||||||
"""Start a task.
|
"""Start a task.
|
||||||
|
@ -412,6 +458,15 @@ class Progress:
|
||||||
if refresh:
|
if refresh:
|
||||||
self.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:
|
def refresh(self) -> None:
|
||||||
"""Refresh (render) the progress information."""
|
"""Refresh (render) the progress information."""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from typing import NamedTuple, Optional
|
from typing import NamedTuple, Optional
|
||||||
|
|
||||||
|
from .cells import cell_len, set_cell_size
|
||||||
from .style import Style
|
from .style import Style
|
||||||
|
|
||||||
from itertools import zip_longest
|
from itertools import zip_longest
|
||||||
|
@ -104,7 +105,7 @@ class Segment(NamedTuple):
|
||||||
Returns:
|
Returns:
|
||||||
List[Segment]: A line of segments with the desired length.
|
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]
|
new_line: List[Segment]
|
||||||
|
|
||||||
if line_length < length:
|
if line_length < length:
|
||||||
|
@ -117,13 +118,14 @@ class Segment(NamedTuple):
|
||||||
append = new_line.append
|
append = new_line.append
|
||||||
line_length = 0
|
line_length = 0
|
||||||
for segment in line:
|
for segment in line:
|
||||||
segment_length = len(segment.text)
|
segment_length = cell_len(segment.text)
|
||||||
if line_length + segment_length < length:
|
if line_length + segment_length < length:
|
||||||
append(segment)
|
append(segment)
|
||||||
line_length += segment_length
|
line_length += segment_length
|
||||||
else:
|
else:
|
||||||
text, style = segment
|
text, style = segment
|
||||||
append(cls(text[: length - line_length], style))
|
text = set_cell_size(text, length - line_length)
|
||||||
|
append(cls(text, style))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
new_line = line[:]
|
new_line = line[:]
|
||||||
|
@ -139,7 +141,7 @@ class Segment(NamedTuple):
|
||||||
Returns:
|
Returns:
|
||||||
int: The length of the line.
|
int: The length of the line.
|
||||||
"""
|
"""
|
||||||
return sum(len(text) for text, _ in line)
|
return sum(cell_len(text) for text, _ in line)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_shape(cls, lines: List[List["Segment"]]) -> Tuple[int, int]:
|
def get_shape(cls, lines: List[List["Segment"]]) -> Tuple[int, int]:
|
||||||
|
|
|
@ -73,6 +73,7 @@ class Syntax:
|
||||||
def from_path(
|
def from_path(
|
||||||
cls,
|
cls,
|
||||||
path: str,
|
path: str,
|
||||||
|
encoding: str = "utf-8",
|
||||||
theme: Union[str, PygmentsStyle] = DEFAULT_THEME,
|
theme: Union[str, PygmentsStyle] = DEFAULT_THEME,
|
||||||
dedent: bool = True,
|
dedent: bool = True,
|
||||||
line_numbers: bool = False,
|
line_numbers: bool = False,
|
||||||
|
@ -86,6 +87,7 @@ class Syntax:
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path (str): Path to file to highlight.
|
path (str): Path to file to highlight.
|
||||||
|
encoding (str): Encoding of file.
|
||||||
lexer_name (str): Lexer to use (see https://pygments.org/docs/lexers/)
|
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".
|
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.
|
dedent (bool, optional): Enable stripping of initial whitespace. Defaults to True.
|
||||||
|
@ -99,7 +101,7 @@ class Syntax:
|
||||||
Returns:
|
Returns:
|
||||||
[Syntax]: A Syntax object that may be printed to the console
|
[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()
|
code = code_file.read()
|
||||||
try:
|
try:
|
||||||
lexer = guess_lexer_for_filename(path, code)
|
lexer = guess_lexer_for_filename(path, code)
|
||||||
|
@ -242,7 +244,7 @@ class Syntax:
|
||||||
padding = _Segment(" " * numbers_column_width, background_style)
|
padding = _Segment(" " * numbers_column_width, background_style)
|
||||||
new_line = _Segment("\n")
|
new_line = _Segment("\n")
|
||||||
|
|
||||||
line_pointer = "->" if WINDOWS else "❱ "
|
line_pointer = "❱ "
|
||||||
|
|
||||||
for line_no, line in enumerate(lines, self.start_line + line_offset):
|
for line_no, line in enumerate(lines, self.start_line + line_offset):
|
||||||
wrapped_lines = console.render_lines(
|
wrapped_lines = console.render_lines(
|
||||||
|
|
|
@ -461,8 +461,8 @@ class Table:
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from .console import Console
|
from .console import Console
|
||||||
|
|
||||||
c = Console(width=80)
|
c = Console()
|
||||||
table = Table()
|
table = Table(expand=True)
|
||||||
table.add_column(no_wrap=True)
|
table.add_column(no_wrap=True)
|
||||||
table.add_column()
|
table.add_column()
|
||||||
table.add_row(
|
table.add_row(
|
||||||
|
|
|
@ -23,6 +23,7 @@ if TYPE_CHECKING: # pragma: no cover
|
||||||
RenderableType,
|
RenderableType,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .cells import cell_len
|
||||||
from .containers import Lines
|
from .containers import Lines
|
||||||
from .style import Style
|
from .style import Style
|
||||||
from .segment import Segment
|
from .segment import Segment
|
||||||
|
@ -322,9 +323,9 @@ class Text:
|
||||||
def __measure__(self, console: "Console", max_width: int) -> Measurement:
|
def __measure__(self, console: "Console", max_width: int) -> Measurement:
|
||||||
text = self.text
|
text = self.text
|
||||||
if not text.strip():
|
if not text.strip():
|
||||||
return Measurement(len(text), len(text))
|
return Measurement(cell_len(text), cell_len(text))
|
||||||
max_text_width = max(len(line) for line in text.splitlines())
|
max_text_width = max(cell_len(line) for line in text.splitlines())
|
||||||
min_text_width = max(len(word) for word in text.split())
|
min_text_width = max(cell_len(word) for word in text.split())
|
||||||
return Measurement(min_text_width, max_text_width)
|
return Measurement(min_text_width, max_text_width)
|
||||||
|
|
||||||
def _render_line(
|
def _render_line(
|
||||||
|
|
|
@ -281,7 +281,7 @@ if __name__ == "__main__": # pragma: no cover
|
||||||
console = Console()
|
console = Console()
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
def bar(a):
|
def bar(a): # 这是对亚洲语言支持的测试。面对模棱两可的想法,拒绝猜测的诱惑
|
||||||
print(1 / a)
|
print(1 / a)
|
||||||
|
|
||||||
def foo(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