autosh 0.0.1__py3-none-any.whl → 0.0.3__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.
autosh/md.py DELETED
@@ -1,394 +0,0 @@
1
- import os
2
- from typing import AsyncGenerator, Literal
3
-
4
-
5
- HIGHLIGHT_COLOR_START = "35"
6
- HIGHLIGHT_COLOR_END = "0"
7
-
8
-
9
- class MarkdowmPrinter:
10
- def __init__(self, stream: AsyncGenerator[str, None]):
11
- async def char_stream(stream: AsyncGenerator[str, None]):
12
- async for chunk in stream:
13
- for char in chunk:
14
- yield char
15
-
16
- self.stream = char_stream(stream)
17
- self.__buf: str = ""
18
- self.__eof = False
19
-
20
- def peek(self):
21
- c = self.__buf[0] if len(self.__buf) > 0 else None
22
- return c
23
-
24
- async def __ensure_length(self, n: int):
25
- while (not self.__eof) and len(self.__buf) < n:
26
- try:
27
- self.__buf += await self.stream.__anext__()
28
- except StopAsyncIteration:
29
- self.__eof = True
30
-
31
- async def __check_unordered_list_label(self) -> bool:
32
- if self.__eof:
33
- return False
34
- await self.__ensure_length(2)
35
- buf = self.__buf
36
- if len(buf) < 2:
37
- return False
38
- if buf[0] in ["-", "+", "*"] and buf[1] == " ":
39
- return True
40
- return False
41
-
42
- async def __check_ordered_list_label(self) -> bool:
43
- if self.__eof:
44
- return False
45
- await self.__ensure_length(5)
46
- buf = self.__buf
47
- # \d+\.
48
- if len(buf) == 0:
49
- return False
50
- if not buf[0].isnumeric():
51
- return False
52
- has_dot = False
53
- for i in range(1, 5):
54
- if i >= len(buf):
55
- return False
56
- c = buf[i]
57
- if c == ".":
58
- if has_dot:
59
- return False
60
- has_dot = True
61
- continue
62
- if c == " ":
63
- if has_dot:
64
- return True
65
- return False
66
- if c.isnumeric():
67
- continue
68
- return False
69
-
70
- async def check(self, s: str):
71
- if len(s) == 0:
72
- return True
73
- await self.__ensure_length(len(s))
74
- if len(self.__buf) < len(s):
75
- return False
76
- return self.__buf[0 : len(s)] == s
77
-
78
- async def check_non_paragraph_block_start(self):
79
- await self.__ensure_length(3)
80
- buf = self.__buf[:3] if len(self.__buf) >= 3 else self.__buf
81
- if buf.startswith("```"):
82
- return True
83
- if buf.startswith("---"):
84
- return True
85
- if buf.startswith("> "):
86
- return True
87
- if await self.__check_ordered_list_label():
88
- return True
89
- if await self.__check_unordered_list_label():
90
- return True
91
- return False
92
-
93
- async def next(self):
94
- c = self.__buf[0] if len(self.__buf) > 0 else None
95
- self.__buf = self.__buf[1:] if len(self.__buf) > 0 else ""
96
- if c is None:
97
- return None
98
- if not self.__eof:
99
- try:
100
- self.__buf += await self.stream.__anext__()
101
- except StopAsyncIteration:
102
- self.__eof = True
103
- return c
104
-
105
- def print(self, s: str):
106
- print(s, end="", flush=True)
107
-
108
- async def parse_single_line_text(
109
- self,
110
- outer_is_italic: bool = False,
111
- outer_is_bold: bool = False,
112
- outer_is_dim: bool = False,
113
- ):
114
- styles: list[Literal["code", "bold", "italic", "strike"]] = []
115
-
116
- def find(s: Literal["code", "bold", "italic", "strike"]):
117
- for i in range(len(styles) - 1, -1, -1):
118
- if styles[i] == s:
119
- return i
120
- return None
121
-
122
- def find_italic_first():
123
- for i in range(len(styles) - 1, -1, -1):
124
- if styles[i] == "bold":
125
- return False
126
- if styles[i] == "italic":
127
- return True
128
- return False
129
-
130
- # Remove leading spaces
131
- while self.peek() in [" ", "\t"]:
132
- await self.next()
133
-
134
- while True:
135
- not_code = find("code") is None
136
- c = self.peek()
137
- if c == "\n" or c is None:
138
- await self.next()
139
- self.print("\x1b[0m\n") # Reset all and newline
140
- return
141
- match c:
142
- case "`":
143
- await self.next()
144
- if (i := find("code")) is not None:
145
- if not outer_is_dim:
146
- self.print("\x1b[22m")
147
- if find("bold") is not None or outer_is_bold:
148
- self.print("\x1b[1m")
149
- styles = styles[:i]
150
- else:
151
- self.print("\x1b[2m")
152
- styles.append("code")
153
- # Bold
154
- case "*" if (
155
- not_code and await self.check("**") and not find_italic_first()
156
- ):
157
- await self.next()
158
- await self.next()
159
- # print(">", styles, find("bold"))
160
- if (i := find("bold")) is not None:
161
- if not outer_is_bold:
162
- self.print("\x1b[22m")
163
- styles = styles[:i]
164
- else:
165
- self.print("\x1b[1m")
166
- styles.append("bold")
167
- case "_" if (
168
- not_code and await self.check("__") and not find_italic_first()
169
- ):
170
- await self.next()
171
- await self.next()
172
- if (i := find("bold")) is not None:
173
- if not outer_is_bold:
174
- self.print("\x1b[22m")
175
- styles = styles[:i]
176
- else:
177
- self.print("\x1b[1m")
178
- styles.append("bold")
179
- # Italic
180
- case "*" | "_" if (
181
- not_code
182
- and not await self.check("* ")
183
- and not await self.check("_ ")
184
- ):
185
- await self.next()
186
- if (i := find("italic")) is not None:
187
- if not outer_is_italic:
188
- self.print("\x1b[23m")
189
- styles = styles[:i]
190
- # print(styles, await self.check("**"))
191
- else:
192
- self.print("\x1b[3m")
193
- styles.append("italic")
194
- # Strike through
195
- case "~" if not_code and await self.check("~~"):
196
- await self.next()
197
- await self.next()
198
- if (i := find("strike")) is not None:
199
- self.print("\x1b[29m")
200
- styles = styles[:i]
201
- else:
202
- self.print("\x1b[9m")
203
- styles.append("strike")
204
- case _:
205
- self.print(c)
206
- await self.next()
207
-
208
- async def parse_heading(self):
209
- hashes = 0
210
- while True:
211
- c = await self.next()
212
- if c == "#":
213
- hashes += 1
214
- else:
215
- break
216
- # Start control
217
- match hashes:
218
- case 1:
219
- self.print("\x1b[45;1;2m") # Magenta background, bold, dim
220
- self.print("#" * hashes)
221
- self.print(" \x1b[22;1m") # Reset dim
222
- await self.parse_single_line_text(outer_is_bold=True)
223
- case 2:
224
- self.print("\x1b[35;1;2;4m") # Magenta foreground, bold, dim, underline
225
- self.print("#" * hashes)
226
- self.print(" \x1b[22m\x1b[1m") # Reset dim
227
- await self.parse_single_line_text(outer_is_bold=True)
228
- case 3:
229
- self.print("\x1b[35;1;2m") # Magenta foreground, bold, dim
230
- self.print("#" * hashes)
231
- self.print(" \x1b[22m\x1b[1m") # Reset dim
232
- await self.parse_single_line_text(outer_is_bold=True)
233
- case 4:
234
- self.print("\x1b[35;2;3m") # Magenta foreground, dim, italic
235
- self.print("#" * hashes)
236
- self.print(" \x1b[22m") # Reset dim
237
- await self.parse_single_line_text(outer_is_italic=True)
238
- case _:
239
- self.print("\x1b[2m") # dim
240
- self.print("#" * hashes)
241
- self.print(" \x1b[22m") # Reset dim
242
- await self.parse_single_line_text()
243
- # Stream title
244
-
245
- async def parse_paragraph(self):
246
- while True:
247
- await self.parse_single_line_text()
248
- if self.peek() != "\n" and not await self.check_non_paragraph_block_start():
249
- await self.next()
250
- break
251
- else:
252
- break
253
-
254
- async def parse_multiline_code(self):
255
- # dim
256
- self.print("\x1b[2m")
257
- self.print("```")
258
- await self.next()
259
- await self.next()
260
- await self.next()
261
- while not await self.check("\n```"):
262
- c = await self.next()
263
- if c is None:
264
- self.print("\n")
265
- return
266
- self.print(c)
267
- self.print("\n```\n")
268
- await self.next()
269
- await self.next()
270
- await self.next()
271
- await self.next()
272
-
273
- async def parse_list(self, ordered: bool):
274
- indents = [0]
275
- counter = [1]
276
- # first item
277
- if ordered:
278
- self.print("1. ")
279
- await self.next()
280
- else:
281
- self.print("• ")
282
- await self.next()
283
- await self.parse_single_line_text()
284
- while True:
285
- indent = 0
286
- while self.peek() in [" ", "\t", "\n"]:
287
- if self.peek() in [" ", "\t"]:
288
- indent += 1
289
- if self.peek() == "\n":
290
- indent = 0
291
- await self.next()
292
- if self.peek() is None:
293
- return
294
- if ordered and not await self.__check_ordered_list_label():
295
- return
296
- if not ordered and not await self.__check_unordered_list_label():
297
- return
298
- if not ordered:
299
- await self.next()
300
- else:
301
- while self.peek() is not None and self.peek() != ".":
302
- await self.next()
303
- await self.next()
304
-
305
- depth = None
306
- for i in range(len(indents) - 1):
307
- if indents[i] <= indent and indents[i + 1] > indent:
308
- depth = i
309
- break
310
- if depth is None and indents[-1] + 2 <= indent:
311
- # indent one more level
312
- indents.append(indent)
313
- depth = len(indents) - 1
314
- counter.append(1)
315
- elif depth is None:
316
- # same as last level
317
- depth = len(indents) - 1
318
- counter[depth] += 1
319
- else:
320
- # dedent
321
- indents = indents[: depth + 1]
322
- counter = counter[: depth + 1]
323
- counter[depth] += 1
324
- if not ordered:
325
- self.print(" " * depth + "• ")
326
- else:
327
- self.print(" " * depth + str(counter[depth]) + ". ")
328
- await self.parse_single_line_text()
329
-
330
- async def parse_blockquote(self):
331
- while True:
332
- while self.peek() in [" ", "\t"]:
333
- await self.next()
334
- if self.peek() != ">":
335
- break
336
- await self.next()
337
- self.print("\x1b[1;2m|\x1b[22;2m ")
338
- await self.parse_single_line_text(outer_is_dim=True)
339
-
340
- async def parse_doc(self):
341
- self.__buf = await self.stream.__anext__()
342
- start = True
343
- while True:
344
- # Remove leading spaces and empty lines
345
- indent = 0
346
- while self.peek() in [" ", "\t", "\n"]:
347
- if self.peek() in [" ", "\t"]:
348
- indent += 1
349
- if self.peek() == "\n":
350
- indent = 0
351
- await self.next()
352
- if self.peek() is None:
353
- break
354
- if not start:
355
- self.print("\n")
356
- start = False
357
- match c := self.peek():
358
- case None:
359
- break
360
- # Heading
361
- case "#":
362
- await self.parse_heading()
363
- # Code
364
- case "`" if await self.check("```"):
365
- await self.parse_multiline_code()
366
- # Separator
367
- case _ if await self.check("---"):
368
- await self.next()
369
- await self.next()
370
- await self.next()
371
- width = min(os.get_terminal_size().columns, 80)
372
- self.print("\x1b[2m" + "─" * width + "\x1b[22m\n")
373
- # Unordered list
374
- case _ if await self.__check_unordered_list_label():
375
- await self.parse_list(False)
376
- # Ordered list
377
- case _ if await self.__check_ordered_list_label():
378
- await self.parse_list(True)
379
- # Blockquote
380
- case ">":
381
- await self.parse_blockquote()
382
- # Normal paragraph
383
- case _:
384
- await self.parse_paragraph()
385
- self.print("\x1b[0m\x1b[0m\x1b[0m") # Reset all
386
- self.print("\x1b[0m") # Reset all
387
-
388
- def __await__(self):
389
- return self.parse_doc().__await__()
390
-
391
-
392
- async def stream_md(stream: AsyncGenerator[str, None]):
393
- mp = MarkdowmPrinter(stream)
394
- await mp.parse_doc()
@@ -1,17 +0,0 @@
1
- autosh/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- autosh/config.py,sha256=XHVICbostoBn01kyzIT5IMvqwpyJDYNYP7awOfIljfw,2141
3
- autosh/main.py,sha256=myRolKqfHyQEgujqmUndDSm9J0C21w3zqA9-SYNX1kY,4961
4
- autosh/md.py,sha256=306onJ24sqYXmDpr_hLaqgo9mbQuZr18vEEDOGvqemI,13729
5
- autosh/session.py,sha256=fDPM9addcTDmCPysaqqd_z3rEseLI2tIa_dLfn3o5Uc,6800
6
- autosh/plugins/__init__.py,sha256=yOTobuyYFpUWl5BCowGzJG8rZX2Whlagail_QyHVlo4,2487
7
- autosh/plugins/calc.py,sha256=qo0EajIpNPv9PtLNLygyEjVaxo1F6_S62kmoJZq5oLM,581
8
- autosh/plugins/cli.py,sha256=joMxIIcNL0y7F9k4e9c6Pu6zmOmGb0pSsR8zFMB7J_0,7366
9
- autosh/plugins/clock.py,sha256=GGi0HAG6f6-FP1qqGoyCcUj11q_VnkaGArumsMk0CkY,542
10
- autosh/plugins/code.py,sha256=0JwFzq6ejgbisCqBm_RG1r1WEVNou64ue-siVIpvZqs,2291
11
- autosh/plugins/search.py,sha256=1d3Gqq6uXu0ntTBpw44Ab_haAySvZLMj3e2MQd3DHO0,2736
12
- autosh/plugins/web.py,sha256=lmD2JnsqVI1qKgSFrk39851jCZoPyPRaVvHeEFYXylA,2597
13
- autosh-0.0.1.dist-info/METADATA,sha256=oXsOPAcYzBj7xKTsnpJcauOJFM2CKTFZAfTIUaJf4wU,1720
14
- autosh-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
- autosh-0.0.1.dist-info/entry_points.txt,sha256=BV7bzUnxG6Z5InEkrfajGCxjooYORC5tZDDZctOPenQ,67
16
- autosh-0.0.1.dist-info/licenses/LICENSE,sha256=BnLDJsIJe-Dm18unR9DOoSv7QOfAz6LeIQc1yHAjxp0,1066
17
- autosh-0.0.1.dist-info/RECORD,,
File without changes