omdev 0.0.0.dev457__py3-none-any.whl → 0.0.0.dev459__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,299 +0,0 @@
1
- # @omlish-precheck-allow-any-unicode
2
- import typing as ta
3
-
4
- from ... import ptk
5
- from .border import DoubleBorder
6
- from .border import SquareBorder
7
- from .utils import FormattedTextAlign
8
- from .utils import add_border
9
- from .utils import align
10
- from .utils import apply_style
11
- from .utils import indent
12
- from .utils import lex
13
- from .utils import strip
14
- from .utils import wrap
15
-
16
-
17
- if ta.TYPE_CHECKING:
18
- from markdown_it.token import Token
19
-
20
-
21
- TagRule: ta.TypeAlias = ta.Callable[
22
- [
23
- ptk.StyleAndTextTuples,
24
- int,
25
- int,
26
- 'Token',
27
- ],
28
- ptk.StyleAndTextTuples,
29
- ]
30
-
31
-
32
- ##
33
-
34
-
35
- def h1(
36
- ft: ptk.StyleAndTextTuples,
37
- width: int,
38
- left: int,
39
- token: 'Token',
40
- ) -> ptk.StyleAndTextTuples:
41
- """Format a top-level heading wrapped and centered with a full width double border."""
42
-
43
- ft = wrap(ft, width - 4)
44
- ft = align(FormattedTextAlign.CENTER, ft, width=width - 4)
45
- ft = add_border(ft, width, style='class:md.h1.border', border=DoubleBorder)
46
- ft.append(('', '\n\n'))
47
- return ft
48
-
49
-
50
- def h2(
51
- ft: ptk.StyleAndTextTuples,
52
- width: int,
53
- left: int,
54
- token: 'Token',
55
- ) -> ptk.StyleAndTextTuples:
56
- """Format a 2nd-level headding wrapped and centered with a double border."""
57
-
58
- ft = wrap(ft, width=width - 4)
59
- ft = align(FormattedTextAlign.CENTER, ft)
60
- ft = add_border(ft, style='class:md.h2.border', border=SquareBorder)
61
- ft = align(FormattedTextAlign.CENTER, ft, width=width)
62
- ft.append(('', '\n\n'))
63
- return ft
64
-
65
-
66
- def h(
67
- ft: ptk.StyleAndTextTuples,
68
- width: int,
69
- left: int,
70
- token: 'Token',
71
- ) -> ptk.StyleAndTextTuples:
72
- """Format headings wrapped and centeredr."""
73
-
74
- ft = wrap(ft, width)
75
- ft = align(FormattedTextAlign.CENTER, ft, width=width)
76
- ft.append(('', '\n\n'))
77
- return ft
78
-
79
-
80
- def p(
81
- ft: ptk.StyleAndTextTuples,
82
- width: int,
83
- left: int,
84
- token: 'Token',
85
- ) -> ptk.StyleAndTextTuples:
86
- """Format paragraphs wrapped."""
87
-
88
- ft = wrap(ft, width)
89
- ft.append(('', '\n' if token.hidden else '\n\n'))
90
- return ft
91
-
92
-
93
- def ul(
94
- ft: ptk.StyleAndTextTuples,
95
- width: int,
96
- left: int,
97
- token: 'Token',
98
- ) -> ptk.StyleAndTextTuples:
99
- """Format unordered lists."""
100
-
101
- ft.append(('', '\n'))
102
- return ft
103
-
104
-
105
- def ol(
106
- ft: ptk.StyleAndTextTuples,
107
- width: int,
108
- left: int,
109
- token: 'Token',
110
- ) -> ptk.StyleAndTextTuples:
111
- """Formats ordered lists."""
112
-
113
- ft.append(('', '\n'))
114
- return ft
115
-
116
-
117
- def li(
118
- ft: ptk.StyleAndTextTuples,
119
- width: int,
120
- left: int,
121
- token: 'Token',
122
- ) -> ptk.StyleAndTextTuples:
123
- """Formats list items."""
124
-
125
- ft = strip(ft)
126
-
127
- # Determine if this is an ordered or unordered list
128
- if token.attrs.get('data-list-type') == 'ol':
129
- margin_style = 'class:md.ol.margin'
130
- else:
131
- margin_style = 'class:md.ul.margin'
132
-
133
- # Get the margin (potentially contains aligned item numbers)
134
- margin = str(token.attrs.get('data-margin', '•'))
135
-
136
- # We put a speace each side of the margin
137
- ft = indent(ft, margin=' ' * (len(margin) + 2), style=margin_style)
138
- ft[0] = (ft[0][0], f' {margin} ')
139
-
140
- ft.append(('', '\n'))
141
- return ft
142
-
143
-
144
- def hr(
145
- ft: ptk.StyleAndTextTuples,
146
- width: int,
147
- left: int,
148
- token: 'Token',
149
- ) -> ptk.StyleAndTextTuples:
150
- """Format horizontal rules."""
151
-
152
- ft = [
153
- ('class:md.hr', '─' * width),
154
- ('', '\n\n'),
155
- ]
156
- return ft
157
-
158
-
159
- def br(
160
- ft: ptk.StyleAndTextTuples,
161
- width: int,
162
- left: int,
163
- token: 'Token',
164
- ) -> ptk.StyleAndTextTuples:
165
- """Format line breaks."""
166
-
167
- return [('', '\n')]
168
-
169
-
170
- def blockquote(
171
- ft: ptk.StyleAndTextTuples,
172
- width: int,
173
- left: int,
174
- token: 'Token',
175
- ) -> ptk.StyleAndTextTuples:
176
- """Format blockquotes with a solid left margin."""
177
-
178
- ft = strip(ft)
179
- ft = indent(ft, margin='▌ ', style='class:md.blockquote.margin')
180
- ft.append(('', '\n\n'))
181
- return ft
182
-
183
-
184
- def code(
185
- ft: ptk.StyleAndTextTuples,
186
- width: int,
187
- left: int,
188
- token: 'Token',
189
- ) -> ptk.StyleAndTextTuples:
190
- """Format inline code, and lexes and formats code blocks with a border."""
191
-
192
- if token.block:
193
- ft = strip(ft, left=False, right=True, char='\n')
194
- ft = lex(ft, lexer_name=token.info)
195
- ft = align(FormattedTextAlign.LEFT, ft, width - 4)
196
- ft = add_border(ft, width, style='class:md.code.border', border=SquareBorder)
197
- ft.append(('', '\n\n'))
198
- else:
199
- ft = apply_style(ft, style='class:md.code.inline')
200
-
201
- return ft
202
-
203
-
204
- def math(
205
- ft: ptk.StyleAndTextTuples,
206
- width: int,
207
- left: int,
208
- token: 'Token',
209
- ) -> ptk.StyleAndTextTuples:
210
- """Format inline maths, and quotes math blocks."""
211
-
212
- if token.block:
213
- return blockquote(ft, width - 2, left, token)
214
- else:
215
- return ft
216
-
217
-
218
- def a(
219
- ft: ptk.StyleAndTextTuples,
220
- width: int,
221
- left: int,
222
- token: 'Token',
223
- ) -> ptk.StyleAndTextTuples:
224
- """Format hyperlinks and adds link escape sequences."""
225
-
226
- result: ptk.StyleAndTextTuples = []
227
- href = token.attrs.get('href')
228
- if href:
229
- result.append(('[ZeroWidthEscape]', f'\x1b]8;;{href}\x1b\\'))
230
- result += ft
231
- if href:
232
- result.append(('[ZeroWidthEscape]', '\x1b]8;;\x1b\\'))
233
- return result
234
-
235
-
236
- def img(
237
- ft: ptk.StyleAndTextTuples,
238
- width: int,
239
- left: int,
240
- token: 'Token',
241
- ) -> ptk.StyleAndTextTuples:
242
- """Format image titles."""
243
-
244
- bounds = ('', '')
245
- if not ptk.to_plain_text(ft):
246
- # Add fallback text if there is no image title
247
- title = str(token.attrs.get('alt'))
248
-
249
- # Try getting the filename
250
- src = str(token.attrs.get('src', ''))
251
- if not title and not src.startswith('data:'):
252
- title = src.rsplit('/', 1)[-1]
253
- if not title:
254
- title = 'Image'
255
- ft = [('class:md.img', title)]
256
-
257
- # Add the sunrise emoji to represent an image. I would use :framed_picture:, but it requires multiple code-points
258
- # and causes breakage in many terminals
259
- result = [('class:md.img', '🌄 '), *ft]
260
- result = apply_style(result, style='class:md.img')
261
- result = [
262
- ('class:md.img.border', f'{bounds[0]}'),
263
- *result,
264
- ('class:md.img.border', f'{bounds[1]}'),
265
- ]
266
- return result
267
-
268
-
269
- ##
270
-
271
-
272
- # Maps HTML tag names to formatting functions. Functionality can be extended by modifying this dictionary
273
- TAG_RULES: ta.Mapping[str, TagRule] = {
274
- 'h1': h1,
275
- 'h2': h2,
276
- 'h3': h,
277
- 'h4': h,
278
- 'h5': h,
279
- 'h6': h,
280
- 'p': p,
281
- 'ul': ul,
282
- 'ol': ol,
283
- 'li': li,
284
- 'hr': hr,
285
- 'br': br,
286
- 'blockquote': blockquote,
287
- 'code': code,
288
- 'math': math,
289
- 'a': a,
290
- 'img': img,
291
- }
292
-
293
-
294
- # Mapping showing how much width the formatting of block elements used. This is used to reduce the available width when
295
- # rendering child elements
296
- TAG_INSETS = {
297
- 'li': 3,
298
- 'blockquote': 2,
299
- }
@@ -1,366 +0,0 @@
1
- # @omlish-precheck-allow-any-unicode
2
- import enum
3
- import typing as ta
4
-
5
- from pygments.lexers import get_lexer_by_name
6
- from pygments.util import ClassNotFound
7
-
8
- from ... import ptk
9
- from .border import Border
10
- from .border import SquareBorder
11
-
12
-
13
- ##
14
-
15
-
16
- class FormattedTextAlign(enum.Enum):
17
- """Alignment of formatted text."""
18
-
19
- LEFT = 'LEFT'
20
- RIGHT = 'RIGHT'
21
- CENTER = 'CENTER'
22
-
23
-
24
- def last_line_length(ft: ptk.StyleAndTextTuples) -> int:
25
- """Calculate the length of the last line in formatted text."""
26
-
27
- line: ptk.StyleAndTextTuples = []
28
- for style, text, *_ in ft[::-1]:
29
- index = text.find('\n')
30
- line.append((style, text[index + 1:]))
31
- if index > -1:
32
- break
33
-
34
- return ptk.fragment_list_width(line)
35
-
36
-
37
- def max_line_width(ft: ptk.StyleAndTextTuples) -> int:
38
- """Calculate the length of the longest line in formatted text."""
39
-
40
- return max(ptk.fragment_list_width(line) for line in ptk.split_lines(ft))
41
-
42
-
43
- def fragment_list_to_words(fragments: ptk.StyleAndTextTuples) -> ta.Iterator[ptk.OneStyleAndTextTuple]:
44
- """Split formatted text into word fragments."""
45
-
46
- for style, string, *mouse_handler in fragments:
47
- parts = string.split(' ')
48
- for part in parts[:-1]:
49
- yield ta.cast('ptk.OneStyleAndTextTuple', (style, part, *mouse_handler))
50
- yield ta.cast('ptk.OneStyleAndTextTuple', (style, ' ', *mouse_handler))
51
-
52
- yield ta.cast('ptk.OneStyleAndTextTuple', (style, parts[-1], *mouse_handler))
53
-
54
-
55
- def apply_style(ft: ptk.StyleAndTextTuples, style: str) -> ptk.StyleAndTextTuples:
56
- """Apply a style to formatted text."""
57
-
58
- return [
59
- (
60
- f'{fragment_style} {style}'
61
- if '[ZeroWidthEscape]' not in fragment_style else fragment_style,
62
- text,
63
- )
64
- for (fragment_style, text, *_) in ft
65
- ]
66
-
67
-
68
- def strip(
69
- ft: ptk.StyleAndTextTuples,
70
- left: bool = True,
71
- right: bool = True,
72
- char: str | None = None,
73
- ) -> ptk.StyleAndTextTuples:
74
- """
75
- Strip whitespace (or a given character) from the ends of formatted text.
76
-
77
- Args:
78
- ft: The formatted text to strip
79
- left: If :py:const:`True`, strip from the left side of the input
80
- right: If :py:const:`True`, strip from the right side of the input
81
- char: The character to strip. If :py:const:`None`, strips whitespace
82
- Returns:
83
- The stripped formatted text
84
- """
85
-
86
- result = ft[:]
87
- for toggle, index, strip_func in [
88
- (left, 0, str.lstrip),
89
- (right, -1, str.rstrip),
90
- ]:
91
- if result and toggle:
92
- text = strip_func(result[index][1], char)
93
-
94
- while result and not text:
95
- del result[index]
96
- if not result:
97
- break
98
-
99
- text = strip_func(result[index][1], char)
100
-
101
- if result and '[ZeroWidthEscape]' not in result[index][0]:
102
- result[index] = (result[index][0], text)
103
-
104
- return result
105
-
106
-
107
- def truncate(
108
- ft: ptk.StyleAndTextTuples,
109
- width: int,
110
- style: str = '',
111
- placeholder: str = '…',
112
- ) -> ptk.StyleAndTextTuples:
113
- """
114
- Truncates all lines at a given length.
115
-
116
- Args:
117
- ft: The formatted text to truncate
118
- width: The width at which to truncate the text
119
- style: The style to apply to the truncation placeholder. The style of the truncated text will be used if not
120
- provided
121
- placeholder: The string that will appear at the end of a truncated line
122
- Returns:
123
- The truncated formatted text
124
- """
125
-
126
- result: ptk.StyleAndTextTuples = []
127
- phw = sum(ptk.get_cwidth(c) for c in placeholder)
128
- for line in ptk.split_lines(ft):
129
- used_width = 0
130
- for item in line:
131
- fragment_width = sum(
132
- ptk.get_cwidth(c)
133
- for c in item[1]
134
- if '[ZeroWidthEscape]' not in item[0]
135
- )
136
-
137
- if used_width + fragment_width > width - phw:
138
- remaining_width = width - used_width - fragment_width - phw
139
- result.append((item[0], item[1][:remaining_width]))
140
- result.append((style or item[0], placeholder))
141
- break
142
-
143
- result.append(item)
144
- used_width += fragment_width
145
-
146
- result.append(('', '\n'))
147
-
148
- result.pop()
149
- return result
150
-
151
-
152
- def wrap(
153
- ft: ptk.StyleAndTextTuples,
154
- width: int,
155
- style: str = '',
156
- placeholder: str = '…',
157
- ) -> ptk.StyleAndTextTuples:
158
- """
159
- Wraps formatted text at a given width. If words are longer than the given line they will be truncated
160
-
161
- Args:
162
- ft: The formatted text to wrap
163
- width: The width at which to wrap the text
164
- style: The style to apply to the truncation placeholder
165
- placeholder: The string that will appear at the end of a truncated line
166
- Returns:
167
- The wrapped formatted text
168
- """
169
-
170
- result: ptk.StyleAndTextTuples = []
171
- lines = list(ptk.split_lines(ft))
172
- for i, line in enumerate(lines):
173
- if ptk.fragment_list_width(line) <= width:
174
- result += line
175
- if i < len(lines) - 1:
176
- result.append(('', '\n'))
177
- continue
178
-
179
- used_width = 0
180
- for item in fragment_list_to_words(line):
181
- fragment_width = sum(
182
- ptk.get_cwidth(c)
183
- for c in item[1]
184
- if '[ZeroWidthEscape]' not in item[0]
185
- )
186
-
187
- # Start a new line we are at the end
188
- if used_width + fragment_width > width and used_width > 0:
189
- # Remove trailing whitespace
190
- result = strip(result, left=False)
191
- result.append(('', '\n'))
192
- used_width = 0
193
-
194
- # Truncate words longer than a line
195
- if fragment_width > width and used_width == 0:
196
- result += truncate([item], width, style, placeholder)
197
- used_width += fragment_width
198
-
199
- # Left-strip words at the start of a line
200
- elif used_width == 0:
201
- result += strip([item], right=False)
202
- used_width += fragment_width
203
-
204
- # Otherwise just add the word to the line
205
- else:
206
- result.append(item)
207
- used_width += fragment_width
208
-
209
- return result
210
-
211
-
212
- def align(
213
- how: FormattedTextAlign,
214
- ft: ptk.StyleAndTextTuples,
215
- width: int | None = None,
216
- style: str = '',
217
- placeholder: str = '…',
218
- ) -> ptk.StyleAndTextTuples:
219
- """
220
- Align formatted text at a given width.
221
-
222
- Args:
223
- how: The alignment direction
224
- ft: The formatted text to strip
225
- width: The width to which the output should be padded. If :py:const:`None`, the length of the longest line is
226
- used
227
- style: The style to apply to the padding
228
- placeholder: The string that will appear at the end of a truncated line
229
- Returns:
230
- The aligned formatted text
231
- """
232
-
233
- lines = ptk.split_lines(ft)
234
- if width is None:
235
- lines = [strip(line) for line in ptk.split_lines(ft)]
236
- width = max(ptk.fragment_list_width(line) for line in lines)
237
-
238
- result: ptk.StyleAndTextTuples = []
239
- for line in lines:
240
- line_width = ptk.fragment_list_width(line)
241
-
242
- # Truncate the line if it is too long
243
- if line_width > width:
244
- result += truncate(line, width, style, placeholder)
245
-
246
- else:
247
- pad_left = pad_right = 0
248
-
249
- if how == FormattedTextAlign.CENTER:
250
- pad_left = (width - line_width) // 2
251
- pad_right = width - line_width - pad_left
252
-
253
- elif how == FormattedTextAlign.LEFT:
254
- pad_right = width - line_width
255
-
256
- elif how == FormattedTextAlign.RIGHT:
257
- pad_left = width - line_width
258
-
259
- if pad_left:
260
- result.append((style, ' ' * pad_left))
261
-
262
- result += line
263
-
264
- if pad_right:
265
- result.append((style, ' ' * pad_right))
266
-
267
- result.append((style, '\n'))
268
-
269
- result.pop()
270
- return result
271
-
272
-
273
- def indent(
274
- ft: ptk.StyleAndTextTuples,
275
- margin: str = ' ',
276
- style: str = '',
277
- skip_first: bool = False,
278
- ) -> ptk.StyleAndTextTuples:
279
- """
280
- Indents formatted text with a given margin.
281
-
282
- Args:
283
- ft: The formatted text to strip
284
- margin: The margin string to add
285
- style: The style to apply to the margin
286
- skip_first: If :py:const:`True`, the first line is skipped
287
- Returns:
288
- The indented formatted text
289
- """
290
-
291
- result: ptk.StyleAndTextTuples = []
292
- for i, line in enumerate(ptk.split_lines(ft)):
293
- if not (i == 0 and skip_first):
294
- result.append((style, margin))
295
- result += line
296
- result.append(('', '\n'))
297
-
298
- result.pop()
299
- return result
300
-
301
-
302
- def add_border(
303
- ft: ptk.StyleAndTextTuples,
304
- width: int | None = None,
305
- style: str = '',
306
- border: type[Border] = SquareBorder,
307
- ) -> ptk.StyleAndTextTuples:
308
- """
309
- Adds a border around formatted text.
310
-
311
- Args:
312
- ft: The formatted text to enclose with a border
313
- width: The target width of the output including the border
314
- style: The style to apply to the border
315
- border: The border to apply
316
- Returns:
317
- The indented formatted text
318
- """
319
-
320
- # if border is None:
321
- # See mypy issue #4236
322
- # border = cast("Type[Border]", Border)
323
- if width is None:
324
- width = max_line_width(ft) + 4
325
-
326
- # ft = align(FormattedTextAlign.LEFT, ft, width - 4)
327
- result: ptk.StyleAndTextTuples = []
328
-
329
- result.append((
330
- style,
331
- border.TOP_LEFT + border.HORIZONTAL * (width - 2) + border.TOP_RIGHT + '\n',
332
- ))
333
-
334
- for line in ptk.split_lines(ft):
335
- result += [
336
- (style, border.VERTICAL),
337
- ('', ' '),
338
- *line,
339
- ('', ' '),
340
- (style, border.VERTICAL + '\n'),
341
- ]
342
-
343
- result.append((
344
- style,
345
- border.BOTTOM_LEFT + border.HORIZONTAL * (width - 2) + border.BOTTOM_RIGHT,
346
- ))
347
-
348
- return result
349
-
350
-
351
- def lex(ft: ptk.StyleAndTextTuples, lexer_name: str) -> ptk.StyleAndTextTuples:
352
- """Format formatted text using a named :py:mod:`pygments` lexer."""
353
-
354
- from prompt_toolkit.lexers.pygments import _token_cache # noqa
355
-
356
- text = ptk.fragment_list_to_text(ft)
357
-
358
- try:
359
- lexer = get_lexer_by_name(lexer_name)
360
- except ClassNotFound:
361
- return ft
362
- else:
363
- return [
364
- (_token_cache[t], v)
365
- for _, t, v in lexer.get_tokens_unprocessed(text)
366
- ]