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/config-template.toml +13 -0
- autosh/config.py +15 -4
- autosh/main.py +7 -8
- autosh/md/__init__.py +8 -0
- autosh/md/inline_text.py +214 -0
- autosh/md/printer.py +301 -0
- autosh/md/state.py +136 -0
- autosh/md/stream.py +107 -0
- autosh/plugins/__init__.py +2 -2
- autosh/plugins/cli.py +42 -0
- autosh/session.py +74 -7
- {autosh-0.0.1.dist-info → autosh-0.0.3.dist-info}/METADATA +8 -4
- autosh-0.0.3.dist-info/RECORD +22 -0
- autosh/md.py +0 -394
- autosh-0.0.1.dist-info/RECORD +0 -17
- {autosh-0.0.1.dist-info → autosh-0.0.3.dist-info}/WHEEL +0 -0
- {autosh-0.0.1.dist-info → autosh-0.0.3.dist-info}/entry_points.txt +0 -0
- {autosh-0.0.1.dist-info → autosh-0.0.3.dist-info}/licenses/LICENSE +0 -0
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()
|
autosh-0.0.1.dist-info/RECORD
DELETED
@@ -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
|
File without changes
|
File without changes
|