Skip to content

Commit c53e20c

Browse files
authored
Add support for ANSI dim text formatting (#1999)
1 parent d997aab commit c53e20c

File tree

9 files changed

+97
-3
lines changed

9 files changed

+97
-3
lines changed

src/prompt_toolkit/formatted_text/ansi.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def __init__(self, value: str) -> None:
3737
self._color: str | None = None
3838
self._bgcolor: str | None = None
3939
self._bold = False
40+
self._dim = False
4041
self._underline = False
4142
self._strike = False
4243
self._italic = False
@@ -153,8 +154,8 @@ def _select_graphic_rendition(self, attrs: list[int]) -> None:
153154
self._bgcolor = _bg_colors[attr]
154155
elif attr == 1:
155156
self._bold = True
156-
# elif attr == 2:
157-
# self._faint = True
157+
elif attr == 2:
158+
self._dim = True
158159
elif attr == 3:
159160
self._italic = True
160161
elif attr == 4:
@@ -171,6 +172,7 @@ def _select_graphic_rendition(self, attrs: list[int]) -> None:
171172
self._strike = True
172173
elif attr == 22:
173174
self._bold = False # Normal intensity
175+
self._dim = False
174176
elif attr == 23:
175177
self._italic = False
176178
elif attr == 24:
@@ -188,6 +190,7 @@ def _select_graphic_rendition(self, attrs: list[int]) -> None:
188190
self._color = None
189191
self._bgcolor = None
190192
self._bold = False
193+
self._dim = False
191194
self._underline = False
192195
self._strike = False
193196
self._italic = False
@@ -232,6 +235,8 @@ def _create_style_string(self) -> str:
232235
result.append("bg:" + self._bgcolor)
233236
if self._bold:
234237
result.append("bold")
238+
if self._dim:
239+
result.append("dim")
235240
if self._underline:
236241
result.append("underline")
237242
if self._strike:

src/prompt_toolkit/output/vt100.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def __missing__(self, value: tuple[int, int, int]) -> int:
257257
class _EscapeCodeCache(Dict[Attrs, str]):
258258
"""
259259
Cache for VT100 escape codes. It maps
260-
(fgcolor, bgcolor, bold, underline, strike, reverse) tuples to VT100
260+
(fgcolor, bgcolor, bold, underline, strike, italic, blink, reverse, hidden, dim) tuples to VT100
261261
escape sequences.
262262
263263
:param true_color: When True, use 24bit colors instead of 256 colors.
@@ -277,13 +277,16 @@ def __missing__(self, attrs: Attrs) -> str:
277277
blink,
278278
reverse,
279279
hidden,
280+
dim,
280281
) = attrs
281282
parts: list[str] = []
282283

283284
parts.extend(self._colors_to_code(fgcolor or "", bgcolor or ""))
284285

285286
if bold:
286287
parts.append("1")
288+
if dim:
289+
parts.append("2")
287290
if italic:
288291
parts.append("3")
289292
if blink:

src/prompt_toolkit/output/win32.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None:
293293
blink,
294294
reverse,
295295
hidden,
296+
dim,
296297
) = attrs
297298
self._hidden = bool(hidden)
298299

src/prompt_toolkit/styles/base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Attrs(NamedTuple):
2929
blink: bool | None
3030
reverse: bool | None
3131
hidden: bool | None
32+
dim: bool | None
3233

3334

3435
"""
@@ -41,6 +42,7 @@ class Attrs(NamedTuple):
4142
:param blink: Boolean
4243
:param reverse: Boolean
4344
:param hidden: Boolean
45+
:param dim: Boolean
4446
"""
4547

4648
#: The default `Attrs`.
@@ -54,6 +56,7 @@ class Attrs(NamedTuple):
5456
blink=False,
5557
reverse=False,
5658
hidden=False,
59+
dim=False,
5760
)
5861

5962

src/prompt_toolkit/styles/style.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def parse_color(text: str) -> str:
8888
blink=None,
8989
reverse=None,
9090
hidden=None,
91+
dim=None,
9192
)
9293

9394

@@ -151,6 +152,10 @@ def _parse_style_str(style_str: str) -> Attrs:
151152
attrs = attrs._replace(hidden=True)
152153
elif part == "nohidden":
153154
attrs = attrs._replace(hidden=False)
155+
elif part == "dim":
156+
attrs = attrs._replace(dim=True)
157+
elif part == "nodim":
158+
attrs = attrs._replace(dim=False)
154159

155160
# Pygments properties that we ignore.
156161
elif part in ("roman", "sans", "mono"):
@@ -345,6 +350,7 @@ def _or(*values: _T) -> _T:
345350
blink=_or(False, *[a.blink for a in list_of_attrs]),
346351
reverse=_or(False, *[a.reverse for a in list_of_attrs]),
347352
hidden=_or(False, *[a.hidden for a in list_of_attrs]),
353+
dim=_or(False, *[a.dim for a in list_of_attrs]),
348354
)
349355

350356

tests/test_formatted_text.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,46 @@ def test_ansi_formatting():
8989
assert isinstance(to_formatted_text(value), FormattedText)
9090

9191

92+
def test_ansi_dim():
93+
# Test dim formatting
94+
value = ANSI("\x1b[2mhello\x1b[0m")
95+
96+
assert to_formatted_text(value) == [
97+
("dim", "h"),
98+
("dim", "e"),
99+
("dim", "l"),
100+
("dim", "l"),
101+
("dim", "o"),
102+
]
103+
104+
# Test dim with other attributes
105+
value = ANSI("\x1b[1;2;31mhello\x1b[0m")
106+
107+
assert to_formatted_text(value) == [
108+
("ansired bold dim", "h"),
109+
("ansired bold dim", "e"),
110+
("ansired bold dim", "l"),
111+
("ansired bold dim", "l"),
112+
("ansired bold dim", "o"),
113+
]
114+
115+
# Test dim reset with code 22
116+
value = ANSI("\x1b[1;2mhello\x1b[22mworld\x1b[0m")
117+
118+
assert to_formatted_text(value) == [
119+
("bold dim", "h"),
120+
("bold dim", "e"),
121+
("bold dim", "l"),
122+
("bold dim", "l"),
123+
("bold dim", "o"),
124+
("", "w"),
125+
("", "o"),
126+
("", "r"),
127+
("", "l"),
128+
("", "d"),
129+
]
130+
131+
92132
def test_ansi_256_color():
93133
assert to_formatted_text(ANSI("\x1b[38;5;124mtest")) == [
94134
("#af0000", "t"),

tests/test_print_formatted_text.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,22 @@ def test_html_with_style():
9191
f.data
9292
== "\x1b[0m\x1b[?7h\x1b[0;32mhello\x1b[0m \x1b[0;1mworld\x1b[0m\r\n\x1b[0m"
9393
)
94+
95+
96+
@pytest.mark.skipif(is_windows(), reason="Doesn't run on Windows yet.")
97+
def test_print_formatted_text_with_dim():
98+
"""
99+
Test that dim formatting works correctly.
100+
"""
101+
f = _Capture()
102+
style = Style.from_dict(
103+
{
104+
"dimtext": "dim",
105+
}
106+
)
107+
tokens = FormattedText([("class:dimtext", "dim text")])
108+
109+
pt_print(tokens, style=style, file=f, color_depth=ColorDepth.DEFAULT)
110+
111+
# Check that the ANSI dim escape code (ESC[2m) is in the output
112+
assert "\x1b[0;2m" in f.data or "\x1b[2m" in f.data

tests/test_style.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def test_style_from_dict():
2222
blink=False,
2323
reverse=False,
2424
hidden=False,
25+
dim=False,
2526
)
2627
assert style.get_attrs_for_style_str("class:a") == expected
2728

@@ -36,6 +37,7 @@ def test_style_from_dict():
3637
blink=True,
3738
reverse=True,
3839
hidden=False,
40+
dim=False,
3941
)
4042
assert style.get_attrs_for_style_str("class:b") == expected
4143

@@ -50,6 +52,7 @@ def test_style_from_dict():
5052
blink=False,
5153
reverse=False,
5254
hidden=False,
55+
dim=False,
5356
)
5457
assert style.get_attrs_for_style_str("#ff0000") == expected
5558

@@ -64,6 +67,7 @@ def test_style_from_dict():
6467
blink=False,
6568
reverse=False,
6669
hidden=False,
70+
dim=False,
6771
)
6872
assert style.get_attrs_for_style_str("class:a #00ff00") == expected
6973

@@ -77,6 +81,7 @@ def test_style_from_dict():
7781
blink=False,
7882
reverse=False,
7983
hidden=False,
84+
dim=False,
8085
)
8186
assert style.get_attrs_for_style_str("#00ff00 class:a") == expected
8287

@@ -101,6 +106,7 @@ def test_class_combinations_1():
101106
blink=False,
102107
reverse=False,
103108
hidden=False,
109+
dim=False,
104110
)
105111
assert style.get_attrs_for_style_str("class:a class:b") == expected
106112
assert style.get_attrs_for_style_str("class:a,b") == expected
@@ -131,6 +137,7 @@ def test_class_combinations_2():
131137
blink=False,
132138
reverse=False,
133139
hidden=False,
140+
dim=False,
134141
)
135142
assert style.get_attrs_for_style_str("class:a class:b") == expected
136143
assert style.get_attrs_for_style_str("class:a,b") == expected
@@ -147,6 +154,7 @@ def test_class_combinations_2():
147154
blink=False,
148155
reverse=False,
149156
hidden=False,
157+
dim=False,
150158
)
151159
assert style.get_attrs_for_style_str("class:b class:a") == expected
152160
assert style.get_attrs_for_style_str("class:b,a") == expected
@@ -173,6 +181,7 @@ def test_substyles():
173181
blink=False,
174182
reverse=False,
175183
hidden=False,
184+
dim=False,
176185
)
177186
assert style.get_attrs_for_style_str("class:a") == expected
178187

@@ -186,6 +195,7 @@ def test_substyles():
186195
blink=False,
187196
reverse=False,
188197
hidden=False,
198+
dim=False,
189199
)
190200
assert style.get_attrs_for_style_str("class:a.b") == expected
191201
assert style.get_attrs_for_style_str("class:a.b.c") == expected
@@ -201,6 +211,7 @@ def test_substyles():
201211
blink=False,
202212
reverse=False,
203213
hidden=False,
214+
dim=False,
204215
)
205216
assert style.get_attrs_for_style_str("class:b") == expected
206217
assert style.get_attrs_for_style_str("class:b.a") == expected
@@ -215,6 +226,7 @@ def test_substyles():
215226
blink=False,
216227
reverse=False,
217228
hidden=False,
229+
dim=False,
218230
)
219231
assert style.get_attrs_for_style_str("class:b.c") == expected
220232
assert style.get_attrs_for_style_str("class:b.c.d") == expected
@@ -234,6 +246,7 @@ def test_swap_light_and_dark_style_transformation():
234246
blink=False,
235247
reverse=False,
236248
hidden=False,
249+
dim=False,
237250
)
238251
after = Attrs(
239252
color="ffbbbb",
@@ -245,6 +258,7 @@ def test_swap_light_and_dark_style_transformation():
245258
blink=False,
246259
reverse=False,
247260
hidden=False,
261+
dim=False,
248262
)
249263

250264
assert transformation.transform_attrs(before) == after
@@ -260,6 +274,7 @@ def test_swap_light_and_dark_style_transformation():
260274
blink=False,
261275
reverse=False,
262276
hidden=False,
277+
dim=False,
263278
)
264279
after = Attrs(
265280
color="ansibrightred",
@@ -271,6 +286,7 @@ def test_swap_light_and_dark_style_transformation():
271286
blink=False,
272287
reverse=False,
273288
hidden=False,
289+
dim=False,
274290
)
275291

276292
assert transformation.transform_attrs(before) == after

tests/test_style_transformation.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def default_attrs():
1717
blink=False,
1818
reverse=False,
1919
hidden=False,
20+
dim=False,
2021
)
2122

2223

0 commit comments

Comments
 (0)