Skip to content

Commit 0980bc3

Browse files
tappletapple-cisco
andauthored
merge html formatting updates into ansi (#1568)
Add support for formatting parameters in ANSI.format(). Co-authored-by: Tapple Fulmer <mfulmer@cisco.com>
1 parent e78e652 commit 0980bc3

File tree

3 files changed

+96
-16
lines changed

3 files changed

+96
-16
lines changed

prompt_toolkit/formatted_text/ansi.py

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from typing import Generator, List, Optional
1+
from string import Formatter
2+
from typing import Generator, List, Optional, Tuple, Union
23

34
from prompt_toolkit.output.vt100 import BG_ANSI_COLORS, FG_ANSI_COLORS
45
from prompt_toolkit.output.vt100 import _256_colors as _256_colors_table
@@ -257,11 +258,17 @@ def format(self, *args: str, **kwargs: str) -> "ANSI":
257258
Like `str.format`, but make sure that the arguments are properly
258259
escaped. (No ANSI escapes can be injected.)
259260
"""
260-
# Escape all the arguments.
261-
args = tuple(ansi_escape(a) for a in args)
262-
kwargs = {k: ansi_escape(v) for k, v in kwargs.items()}
261+
return ANSI(FORMATTER.vformat(self.value, args, kwargs))
263262

264-
return ANSI(self.value.format(*args, **kwargs))
263+
def __mod__(self, value: object) -> "ANSI":
264+
"""
265+
ANSI('<b>%s</b>') % value
266+
"""
267+
if not isinstance(value, tuple):
268+
value = (value,)
269+
270+
value = tuple(ansi_escape(i) for i in value)
271+
return ANSI(self.value % value)
265272

266273

267274
# Mapping of the ANSI color codes to their names.
@@ -275,8 +282,16 @@ def format(self, *args: str, **kwargs: str) -> "ANSI":
275282
_256_colors[i] = "#%02x%02x%02x" % (r, g, b)
276283

277284

278-
def ansi_escape(text: str) -> str:
285+
def ansi_escape(text: object) -> str:
279286
"""
280287
Replace characters with a special meaning.
281288
"""
282-
return text.replace("\x1b", "?").replace("\b", "?")
289+
return str(text).replace("\x1b", "?").replace("\b", "?")
290+
291+
292+
class ANSIFormatter(Formatter):
293+
def format_field(self, value: object, format_spec: str) -> str:
294+
return ansi_escape(format(value, format_spec))
295+
296+
297+
FORMATTER = ANSIFormatter()

prompt_toolkit/formatted_text/html.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def format(self, *args: object, **kwargs: object) -> "HTML":
110110
"""
111111
return HTML(FORMATTER.vformat(self.value, args, kwargs))
112112

113-
def __mod__(self, value: Union[object, Tuple[object, ...]]) -> "HTML":
113+
def __mod__(self, value: object) -> "HTML":
114114
"""
115115
HTML('<b>%s</b>') % value
116116
"""

tests/test_formatted_text.py

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,71 @@ def test_ansi_true_color():
103103
]
104104

105105

106+
def test_ansi_interpolation():
107+
# %-style interpolation.
108+
value = ANSI("\x1b[1m%s\x1b[0m") % "hello\x1b"
109+
assert to_formatted_text(value) == [
110+
("bold", "h"),
111+
("bold", "e"),
112+
("bold", "l"),
113+
("bold", "l"),
114+
("bold", "o"),
115+
("bold", "?"),
116+
]
117+
118+
value = ANSI("\x1b[1m%s\x1b[0m") % ("\x1bhello",)
119+
assert to_formatted_text(value) == [
120+
("bold", "?"),
121+
("bold", "h"),
122+
("bold", "e"),
123+
("bold", "l"),
124+
("bold", "l"),
125+
("bold", "o"),
126+
]
127+
128+
value = ANSI("\x1b[32m%s\x1b[45m%s") % ("He", "\x1bllo")
129+
assert to_formatted_text(value) == [
130+
("ansigreen", "H"),
131+
("ansigreen", "e"),
132+
("ansigreen bg:ansimagenta", "?"),
133+
("ansigreen bg:ansimagenta", "l"),
134+
("ansigreen bg:ansimagenta", "l"),
135+
("ansigreen bg:ansimagenta", "o"),
136+
]
137+
138+
# Format function.
139+
value = ANSI("\x1b[32m{0}\x1b[45m{1}").format("He\x1b", "llo")
140+
assert to_formatted_text(value) == [
141+
("ansigreen", "H"),
142+
("ansigreen", "e"),
143+
("ansigreen", "?"),
144+
("ansigreen bg:ansimagenta", "l"),
145+
("ansigreen bg:ansimagenta", "l"),
146+
("ansigreen bg:ansimagenta", "o"),
147+
]
148+
149+
value = ANSI("\x1b[32m{a}\x1b[45m{b}").format(a="\x1bHe", b="llo")
150+
assert to_formatted_text(value) == [
151+
("ansigreen", "?"),
152+
("ansigreen", "H"),
153+
("ansigreen", "e"),
154+
("ansigreen bg:ansimagenta", "l"),
155+
("ansigreen bg:ansimagenta", "l"),
156+
("ansigreen bg:ansimagenta", "o"),
157+
]
158+
159+
value = ANSI("\x1b[32m{:02d}\x1b[45m{:.3f}").format(3, 3.14159)
160+
assert to_formatted_text(value) == [
161+
("ansigreen", "0"),
162+
("ansigreen", "3"),
163+
("ansigreen bg:ansimagenta", "3"),
164+
("ansigreen bg:ansimagenta", "."),
165+
("ansigreen bg:ansimagenta", "1"),
166+
("ansigreen bg:ansimagenta", "4"),
167+
("ansigreen bg:ansimagenta", "2"),
168+
]
169+
170+
106171
def test_interpolation():
107172
value = Template(" {} ").format(HTML("<b>hello</b>"))
108173

@@ -125,18 +190,18 @@ def test_interpolation():
125190

126191
def test_html_interpolation():
127192
# %-style interpolation.
128-
value = HTML("<b>%s</b>") % "hello"
129-
assert to_formatted_text(value) == [("class:b", "hello")]
193+
value = HTML("<b>%s</b>") % "&hello"
194+
assert to_formatted_text(value) == [("class:b", "&hello")]
130195

131-
value = HTML("<b>%s</b>") % ("hello",)
132-
assert to_formatted_text(value) == [("class:b", "hello")]
196+
value = HTML("<b>%s</b>") % ("<hello>",)
197+
assert to_formatted_text(value) == [("class:b", "<hello>")]
133198

134-
value = HTML("<b>%s</b><u>%s</u>") % ("hello", "world")
135-
assert to_formatted_text(value) == [("class:b", "hello"), ("class:u", "world")]
199+
value = HTML("<b>%s</b><u>%s</u>") % ("<hello>", "</world>")
200+
assert to_formatted_text(value) == [("class:b", "<hello>"), ("class:u", "</world>")]
136201

137202
# Format function.
138-
value = HTML("<b>{0}</b><u>{1}</u>").format("hello", "world")
139-
assert to_formatted_text(value) == [("class:b", "hello"), ("class:u", "world")]
203+
value = HTML("<b>{0}</b><u>{1}</u>").format("'hello'", '"world"')
204+
assert to_formatted_text(value) == [("class:b", "'hello'"), ("class:u", '"world"')]
140205

141206
value = HTML("<b>{a}</b><u>{b}</u>").format(a="hello", b="world")
142207
assert to_formatted_text(value) == [("class:b", "hello"), ("class:u", "world")]

0 commit comments

Comments
 (0)