pytermgui.markup.macros
All PTG-builtin TIM macros.
1"""All PTG-builtin TIM macros.""" 2 3from __future__ import annotations 4 5from random import shuffle 6from typing import Any, TypeVar 7 8from .parsing import parse 9 10DEFAULT_MACROS = {} 11 12MarkupLanguage = Any 13 14 15MacroTemplate = TypeVar("MacroTemplate") 16 17 18def export_macro(func: MacroTemplate) -> MacroTemplate: 19 """A decorator to add a function to `DEFAULT_MACROS`.""" 20 21 name = "_".join(func.__name__.split("_")[1:]) # type: ignore 22 23 DEFAULT_MACROS[f"!{name}"] = func 24 25 return func 26 27 28def apply_default_macros(lang: MarkupLanguage) -> None: 29 """Applies all macros in `DEFAULT_MACROS`. 30 31 Args: 32 lang: The language to apply the macros to. 33 """ 34 35 for name, value in DEFAULT_MACROS.items(): 36 lang.define(name, value) 37 38 39@export_macro 40def macro_upper(text: str) -> str: 41 """Turns the text into uppercase.""" 42 43 return text.upper() 44 45 46@export_macro 47def macro_lower(text: str) -> str: 48 """Turns the text into lowercase.""" 49 50 return text.lower() 51 52 53@export_macro 54def macro_title(text: str) -> str: 55 """Turns the text into titlecase.""" 56 57 return text.title() 58 59 60@export_macro 61def macro_align(width: str, alignment: str, content: str) -> str: 62 """Aligns given text using fstrings. 63 64 Args: 65 width: The width to align to. 66 alignment: One of "left", "center", "right". 67 content: The content to align; implicit argument. 68 """ 69 70 aligner = "<" if alignment == "left" else (">" if alignment == "right" else "^") 71 return f"{content:{aligner}{width}}" 72 73 74@export_macro 75def macro_expand(lang: MarkupLanguage, tag: str) -> str: 76 """Expands a tag alias.""" 77 78 if not tag in lang.user_tags: 79 return tag 80 81 return lang.get_markup(f"\x1b[{lang.user_tags[tag]}m ")[:-1] 82 83 84@export_macro 85def macro_shuffle(item: str) -> str: 86 """Shuffles a string using shuffle.shuffle on its list cast.""" 87 88 shuffled = list(item) 89 shuffle(shuffled) 90 91 return "".join(shuffled) 92 93 94def _apply_colors(colors: list[str] | list[int], item: str) -> str: 95 """Applies the given list of colors to the item, spread out evenly.""" 96 97 blocksize = max(round(len(item) / len(colors)), 1) 98 99 out = "" 100 current_block = 0 101 for i, char in enumerate(item): 102 if i % blocksize == 0 and current_block < len(colors): 103 out += f"[{colors[current_block]}]" 104 current_block += 1 105 106 out += char 107 108 return parse(out + "[/fg]", append_reset=False) + "\x1b[0m" 109 110 111@export_macro 112def macro_rainbow(item: str) -> str: 113 """Creates rainbow-colored text.""" 114 115 colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"] 116 117 return _apply_colors(colors, item) 118 119 120@export_macro 121def macro_gradient(base_str: str, item: str) -> str: 122 """Creates an xterm-256 gradient from a base color. 123 124 This exploits the way the colors are arranged in the xterm color table; every 125 36th color is the next item of a single gradient. 126 127 The start of this given gradient is calculated by decreasing the given base by 36 on 128 every iteration as long as the point is a valid gradient start. 129 130 After that, the 6 colors of this gradient are calculated and applied. 131 """ 132 133 if not base_str.isdigit(): 134 raise ValueError(f"Gradient base has to be a digit, got {base_str}.") 135 136 base = int(base_str) 137 if base < 16 or base > 231: 138 raise ValueError("Gradient base must be between 16 and 232") 139 140 while base > 52: 141 base -= 36 142 143 colors = [] 144 for i in range(6): 145 colors.append(base + 36 * i) 146 147 return _apply_colors(colors, item)
def
export_macro(func: ~MacroTemplate) -> ~MacroTemplate:
19def export_macro(func: MacroTemplate) -> MacroTemplate: 20 """A decorator to add a function to `DEFAULT_MACROS`.""" 21 22 name = "_".join(func.__name__.split("_")[1:]) # type: ignore 23 24 DEFAULT_MACROS[f"!{name}"] = func 25 26 return func
A decorator to add a function to DEFAULT_MACROS
.
def
apply_default_macros(lang: Any) -> None:
29def apply_default_macros(lang: MarkupLanguage) -> None: 30 """Applies all macros in `DEFAULT_MACROS`. 31 32 Args: 33 lang: The language to apply the macros to. 34 """ 35 36 for name, value in DEFAULT_MACROS.items(): 37 lang.define(name, value)
Applies all macros in DEFAULT_MACROS
.
Args
- lang: The language to apply the macros to.
@export_macro
def
macro_upper(text: str) -> str:
40@export_macro 41def macro_upper(text: str) -> str: 42 """Turns the text into uppercase.""" 43 44 return text.upper()
Turns the text into uppercase.
@export_macro
def
macro_lower(text: str) -> str:
47@export_macro 48def macro_lower(text: str) -> str: 49 """Turns the text into lowercase.""" 50 51 return text.lower()
Turns the text into lowercase.
@export_macro
def
macro_title(text: str) -> str:
54@export_macro 55def macro_title(text: str) -> str: 56 """Turns the text into titlecase.""" 57 58 return text.title()
Turns the text into titlecase.
@export_macro
def
macro_align(width: str, alignment: str, content: str) -> str:
61@export_macro 62def macro_align(width: str, alignment: str, content: str) -> str: 63 """Aligns given text using fstrings. 64 65 Args: 66 width: The width to align to. 67 alignment: One of "left", "center", "right". 68 content: The content to align; implicit argument. 69 """ 70 71 aligner = "<" if alignment == "left" else (">" if alignment == "right" else "^") 72 return f"{content:{aligner}{width}}"
Aligns given text using fstrings.
Args
- width: The width to align to.
- alignment: One of "left", "center", "right".
- content: The content to align; implicit argument.
@export_macro
def
macro_expand(lang: Any, tag: str) -> str:
75@export_macro 76def macro_expand(lang: MarkupLanguage, tag: str) -> str: 77 """Expands a tag alias.""" 78 79 if not tag in lang.user_tags: 80 return tag 81 82 return lang.get_markup(f"\x1b[{lang.user_tags[tag]}m ")[:-1]
Expands a tag alias.
@export_macro
def
macro_shuffle(item: str) -> str:
85@export_macro 86def macro_shuffle(item: str) -> str: 87 """Shuffles a string using shuffle.shuffle on its list cast.""" 88 89 shuffled = list(item) 90 shuffle(shuffled) 91 92 return "".join(shuffled)
Shuffles a string using shuffle.shuffle on its list cast.
@export_macro
def
macro_rainbow(item: str) -> str:
112@export_macro 113def macro_rainbow(item: str) -> str: 114 """Creates rainbow-colored text.""" 115 116 colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"] 117 118 return _apply_colors(colors, item)
Creates rainbow-colored text.
@export_macro
def
macro_gradient(base_str: str, item: str) -> str:
121@export_macro 122def macro_gradient(base_str: str, item: str) -> str: 123 """Creates an xterm-256 gradient from a base color. 124 125 This exploits the way the colors are arranged in the xterm color table; every 126 36th color is the next item of a single gradient. 127 128 The start of this given gradient is calculated by decreasing the given base by 36 on 129 every iteration as long as the point is a valid gradient start. 130 131 After that, the 6 colors of this gradient are calculated and applied. 132 """ 133 134 if not base_str.isdigit(): 135 raise ValueError(f"Gradient base has to be a digit, got {base_str}.") 136 137 base = int(base_str) 138 if base < 16 or base > 231: 139 raise ValueError("Gradient base must be between 16 and 232") 140 141 while base > 52: 142 base -= 36 143 144 colors = [] 145 for i in range(6): 146 colors.append(base + 36 * i) 147 148 return _apply_colors(colors, item)
Creates an xterm-256 gradient from a base color.
This exploits the way the colors are arranged in the xterm color table; every 36th color is the next item of a single gradient.
The start of this given gradient is calculated by decreasing the given base by 36 on every iteration as long as the point is a valid gradient start.
After that, the 6 colors of this gradient are calculated and applied.