Breaks a line into a list[str]
with maximum limit
length per line.
It keeps ongoing ANSI sequences between lines, and inserts a reset sequence
at the end of each style-containing line.
At the moment it splits strings exactly on the limit, and not on word
boundaries. That functionality would be preferred, so it will end up being
implemented at some point.
Parameters:
Name |
Type |
Description |
Default |
line |
str
|
The line to split. May or may not contain ANSI sequences. |
required
|
limit |
int
|
The maximum amount of characters allowed in each line, excluding
non-printing sequences. |
required
|
non_first_limit |
int | None
|
The limit after the first line. If not given, defaults
to limit . |
None
|
Source code in pytermgui/helpers.py
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113 | def break_line( # pylint: disable=too-many-branches
line: str, limit: int, non_first_limit: int | None = None, fill: str | None = None
) -> Iterator[str]:
"""Breaks a line into a `list[str]` with maximum `limit` length per line.
It keeps ongoing ANSI sequences between lines, and inserts a reset sequence
at the end of each style-containing line.
At the moment it splits strings exactly on the limit, and not on word
boundaries. That functionality would be preferred, so it will end up being
implemented at some point.
Args:
line: The line to split. May or may not contain ANSI sequences.
limit: The maximum amount of characters allowed in each line, excluding
non-printing sequences.
non_first_limit: The limit after the first line. If not given, defaults
to `limit`.
"""
if line in ["", "\x1b[0m"]:
yield ""
return
def _pad_and_link(line: str, link: str | None) -> str:
count = limit - real_length(line)
if link is not None:
line = LINK_TEMPLATE.format(uri=link, label=line)
if fill is None:
return line
line += count * fill
return line
used = 0
current = ""
sequences = ""
if non_first_limit is None:
non_first_limit = limit
parsers = PARSERS
link = None
for token in tokenize_ansi(line):
if token.is_plain():
for char in token.value:
if char == "\n" or used >= limit:
if sequences != "":
current += "\x1b[0m"
yield _pad_and_link(current, link)
link = None
current = sequences
used = 0
limit = non_first_limit
if char != "\n":
current += char
used += 1
# If the link wasn't yielded along with its token, remove and add it
# to current manually.
if link is not None:
current = current[: -len(token.value)]
current += LINK_TEMPLATE.format(uri=link, label=token.value)
link = None
continue
if token.value == "/":
sequences = "\x1b[0m"
if len(current) > 0:
current += sequences
continue
if token.is_hyperlink():
link = token.value
continue
sequence = parsers[type(token)](token, {}, lambda: line) # type: ignore
sequences += sequence
current += sequence
if current == "":
return
if sequences != "" and not current.endswith("\x1b[0m"):
current += "\x1b[0m"
yield _pad_and_link(current, link)
|