pytermgui.helpers
Helper methods and functions for pytermgui.
1"""Helper methods and functions for pytermgui.""" 2 3from __future__ import annotations 4 5from typing import Iterator 6 7from .colors import Color 8from .ansi_interface import reset 9from .parser import markup, TokenType, Token 10 11__all__ = [ 12 "get_applied_sequences", 13 "break_line", 14] 15 16 17def get_applied_sequences(text: str) -> str: 18 """Extracts ANSI sequences from text. 19 20 Args: 21 text: The text to operate on. 22 23 Returns: 24 All sequences found. 25 """ 26 27 tokens: list[Token] = [] 28 reset_char = reset() 29 for token in markup.tokenize_ansi(text): 30 if not token.ttype is TokenType.UNSETTER: 31 tokens.append(token) 32 continue 33 34 assert token.sequence is not None 35 if token.sequence == reset_char: 36 tokens = [] 37 continue 38 39 name = token.name.lstrip("/") 40 for style in tokens.copy(): 41 if name in ["fg", "bg"] and style.ttype is TokenType.COLOR: 42 color = style.data 43 assert isinstance(color, Color) 44 45 if name == "fg" and not color.background: 46 tokens.remove(style) 47 elif name == "bg" and color.background: 48 tokens.remove(style) 49 50 elif style.name == name: 51 tokens.remove(style) 52 53 continue 54 55 return "".join(token.sequence or "" for token in tokens) 56 57 58def break_line( 59 line: str, limit: int, non_first_limit: int | None = None 60) -> Iterator[str]: 61 """Breaks a line into a `list[str]` with maximum `limit` length per line. 62 63 It keeps ongoing ANSI sequences between lines, and inserts a reset sequence 64 at the end of each style-containing line. 65 66 At the moment it splits strings exactly on the limit, and not on word 67 boundaries. That functionality would be preferred, so it will end up being 68 implemented at some point. 69 70 Args: 71 line: The line to split. May or may not contain ANSI sequences. 72 limit: The maximum amount of characters allowed in each line, excluding 73 non-printing sequences. 74 non_first_limit: The limit after the first line. If not given, defaults 75 to `limit`. 76 """ 77 78 used = 0 79 current = "" 80 sequences = "" 81 82 if non_first_limit is None: 83 non_first_limit = limit 84 85 for token in markup.tokenize_ansi(line): 86 if token.sequence is None: 87 assert isinstance(token.data, str) 88 for char in token.data: 89 if char == "\n" or used >= limit: 90 if sequences != "": 91 current += "\x1b[0m" 92 93 yield current 94 95 current = sequences 96 used = 0 97 98 limit = non_first_limit 99 100 if char != "\n": 101 current += char 102 used += 1 103 104 continue 105 106 if token.sequence == "\x1b[0m": 107 sequences = "\x1b[0m" 108 109 if len(current) > 0: 110 current += sequences 111 112 continue 113 114 sequences += token.sequence 115 current += token.sequence 116 117 if current == "": 118 return 119 120 if sequences != "" and not current.endswith("\x1b[0m"): 121 current += "\x1b[0m" 122 123 yield current
def
get_applied_sequences(text: str) -> str:
18def get_applied_sequences(text: str) -> str: 19 """Extracts ANSI sequences from text. 20 21 Args: 22 text: The text to operate on. 23 24 Returns: 25 All sequences found. 26 """ 27 28 tokens: list[Token] = [] 29 reset_char = reset() 30 for token in markup.tokenize_ansi(text): 31 if not token.ttype is TokenType.UNSETTER: 32 tokens.append(token) 33 continue 34 35 assert token.sequence is not None 36 if token.sequence == reset_char: 37 tokens = [] 38 continue 39 40 name = token.name.lstrip("/") 41 for style in tokens.copy(): 42 if name in ["fg", "bg"] and style.ttype is TokenType.COLOR: 43 color = style.data 44 assert isinstance(color, Color) 45 46 if name == "fg" and not color.background: 47 tokens.remove(style) 48 elif name == "bg" and color.background: 49 tokens.remove(style) 50 51 elif style.name == name: 52 tokens.remove(style) 53 54 continue 55 56 return "".join(token.sequence or "" for token in tokens)
Extracts ANSI sequences from text.
Args
- text: The text to operate on.
Returns
All sequences found.
def
break_line( line: str, limit: int, non_first_limit: int | None = None) -> Iterator[str]:
59def break_line( 60 line: str, limit: int, non_first_limit: int | None = None 61) -> Iterator[str]: 62 """Breaks a line into a `list[str]` with maximum `limit` length per line. 63 64 It keeps ongoing ANSI sequences between lines, and inserts a reset sequence 65 at the end of each style-containing line. 66 67 At the moment it splits strings exactly on the limit, and not on word 68 boundaries. That functionality would be preferred, so it will end up being 69 implemented at some point. 70 71 Args: 72 line: The line to split. May or may not contain ANSI sequences. 73 limit: The maximum amount of characters allowed in each line, excluding 74 non-printing sequences. 75 non_first_limit: The limit after the first line. If not given, defaults 76 to `limit`. 77 """ 78 79 used = 0 80 current = "" 81 sequences = "" 82 83 if non_first_limit is None: 84 non_first_limit = limit 85 86 for token in markup.tokenize_ansi(line): 87 if token.sequence is None: 88 assert isinstance(token.data, str) 89 for char in token.data: 90 if char == "\n" or used >= limit: 91 if sequences != "": 92 current += "\x1b[0m" 93 94 yield current 95 96 current = sequences 97 used = 0 98 99 limit = non_first_limit 100 101 if char != "\n": 102 current += char 103 used += 1 104 105 continue 106 107 if token.sequence == "\x1b[0m": 108 sequences = "\x1b[0m" 109 110 if len(current) > 0: 111 current += sequences 112 113 continue 114 115 sequences += token.sequence 116 current += token.sequence 117 118 if current == "": 119 return 120 121 if sequences != "" and not current.endswith("\x1b[0m"): 122 current += "\x1b[0m" 123 124 yield current
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
.