omdev 0.0.0.dev459__py3-none-any.whl → 0.0.0.dev461__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.
Potentially problematic release.
This version of omdev might be problematic. Click here for more details.
- omdev/__about__.py +1 -1
- omdev/irc/__init__.py +0 -0
- omdev/irc/messages/__init__.py +0 -0
- omdev/irc/messages/base.py +50 -0
- omdev/irc/messages/formats.py +92 -0
- omdev/irc/messages/messages.py +775 -0
- omdev/irc/messages/parsing.py +99 -0
- omdev/irc/numerics/__init__.py +0 -0
- omdev/irc/numerics/formats.py +97 -0
- omdev/irc/numerics/numerics.py +865 -0
- omdev/irc/numerics/types.py +59 -0
- omdev/irc/protocol/LICENSE +11 -0
- omdev/irc/protocol/__init__.py +61 -0
- omdev/irc/protocol/consts.py +6 -0
- omdev/irc/protocol/errors.py +30 -0
- omdev/irc/protocol/message.py +21 -0
- omdev/irc/protocol/nuh.py +55 -0
- omdev/irc/protocol/parsing.py +158 -0
- omdev/irc/protocol/rendering.py +153 -0
- omdev/irc/protocol/tags.py +102 -0
- omdev/irc/protocol/utils.py +30 -0
- omdev/markdown/__init__.py +0 -0
- omdev/markdown/incparse.py +116 -0
- omdev/markdown/tokens.py +51 -0
- omdev/precheck/imports.py +14 -1
- omdev/precheck/main.py +1 -1
- omdev/tui/apps/markdown/cli.py +3 -4
- omdev/tui/rich/__init__.py +33 -0
- omdev/tui/rich/console2.py +20 -0
- omdev/tui/rich/markdown2.py +186 -0
- omdev/tui/textual/__init__.py +9 -0
- {omdev-0.0.0.dev459.dist-info → omdev-0.0.0.dev461.dist-info}/METADATA +4 -4
- {omdev-0.0.0.dev459.dist-info → omdev-0.0.0.dev461.dist-info}/RECORD +37 -10
- {omdev-0.0.0.dev459.dist-info → omdev-0.0.0.dev461.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev459.dist-info → omdev-0.0.0.dev461.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev459.dist-info → omdev-0.0.0.dev461.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev459.dist-info → omdev-0.0.0.dev461.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import dataclasses as dc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
with lang.auto_proxy_import(globals()):
|
|
8
|
+
import markdown_it as md
|
|
9
|
+
import markdown_it.token # noqa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class IncrementalMarkdownParser:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
*,
|
|
19
|
+
parser: ta.Optional['md.MarkdownIt'] = None,
|
|
20
|
+
) -> None:
|
|
21
|
+
super().__init__()
|
|
22
|
+
|
|
23
|
+
if parser is None:
|
|
24
|
+
parser = md.MarkdownIt()
|
|
25
|
+
self._parser = parser
|
|
26
|
+
|
|
27
|
+
self._stable_tokens: list[md.token.Token] = []
|
|
28
|
+
self._buffer = ''
|
|
29
|
+
self._num_stable_lines = 0 # Number of lines in stable tokens
|
|
30
|
+
|
|
31
|
+
class FeedOutput(ta.NamedTuple):
|
|
32
|
+
stable: ta.Sequence['md.token.Token']
|
|
33
|
+
new_stable: ta.Sequence['md.token.Token']
|
|
34
|
+
unstable: ta.Sequence['md.token.Token']
|
|
35
|
+
|
|
36
|
+
def feed2(self, chunk: str) -> FeedOutput:
|
|
37
|
+
self._buffer += chunk
|
|
38
|
+
|
|
39
|
+
# Parse the current buffer
|
|
40
|
+
new_tokens = self._parser.parse(self._buffer)
|
|
41
|
+
|
|
42
|
+
# Adjust ALL tokens to account for stable lines from previous parses (new_tokens have line numbers relative to
|
|
43
|
+
# current buffer)
|
|
44
|
+
adjusted_tokens = self._adjust_token_line_numbers(new_tokens, self._num_stable_lines)
|
|
45
|
+
|
|
46
|
+
# Find stable tokens (all but the last parent and its children)
|
|
47
|
+
stable_count = self._find_stable_token_count(adjusted_tokens)
|
|
48
|
+
|
|
49
|
+
newly_stable: ta.Sequence[md.token.Token]
|
|
50
|
+
if stable_count > 0:
|
|
51
|
+
# Extract newly stable tokens (already have adjusted line numbers)
|
|
52
|
+
newly_stable = adjusted_tokens[:stable_count]
|
|
53
|
+
|
|
54
|
+
# Calculate how many lines these stable tokens cover
|
|
55
|
+
max_line = 0
|
|
56
|
+
for token in newly_stable:
|
|
57
|
+
if token.map:
|
|
58
|
+
max_line = max(max_line, token.map[1])
|
|
59
|
+
|
|
60
|
+
# Update buffer to only contain unstable content
|
|
61
|
+
if max_line > self._num_stable_lines:
|
|
62
|
+
# max_line is absolute, num_stable_lines is the current buffer offset
|
|
63
|
+
lines_to_remove = max_line - self._num_stable_lines
|
|
64
|
+
lines = self._buffer.split('\n')
|
|
65
|
+
self._buffer = '\n'.join(lines[lines_to_remove:])
|
|
66
|
+
|
|
67
|
+
# Store newly stable tokens (with adjusted line numbers)
|
|
68
|
+
self._stable_tokens.extend(newly_stable)
|
|
69
|
+
self._num_stable_lines = max_line
|
|
70
|
+
|
|
71
|
+
else:
|
|
72
|
+
newly_stable = ()
|
|
73
|
+
|
|
74
|
+
return IncrementalMarkdownParser.FeedOutput(
|
|
75
|
+
stable=self._stable_tokens,
|
|
76
|
+
new_stable=newly_stable,
|
|
77
|
+
unstable=adjusted_tokens[stable_count:],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def feed(self, chunk: str) -> list['md.token.Token']:
|
|
81
|
+
out = self.feed2(chunk)
|
|
82
|
+
return [*out.stable, *out.unstable]
|
|
83
|
+
|
|
84
|
+
def _find_stable_token_count(self, tokens: list['md.token.Token']) -> int:
|
|
85
|
+
if not tokens:
|
|
86
|
+
return 0
|
|
87
|
+
|
|
88
|
+
# Find indices of all parent-level tokens (nesting = 0)
|
|
89
|
+
parent_indices = []
|
|
90
|
+
for i, token in enumerate(tokens):
|
|
91
|
+
if token.nesting in (1, 0) and token.level == 0:
|
|
92
|
+
parent_indices.append(i)
|
|
93
|
+
|
|
94
|
+
if len(parent_indices) < 2:
|
|
95
|
+
# Need at least 2 parent tokens to have stable content
|
|
96
|
+
return 0
|
|
97
|
+
|
|
98
|
+
# The last parent and everything after it is unstable. Everything before the second-to-last parent is stable.
|
|
99
|
+
return parent_indices[-2]
|
|
100
|
+
|
|
101
|
+
def _adjust_token_line_numbers(
|
|
102
|
+
self,
|
|
103
|
+
tokens: list['md.token.Token'],
|
|
104
|
+
line_offset: int,
|
|
105
|
+
) -> list['md.token.Token']:
|
|
106
|
+
adjusted = []
|
|
107
|
+
for token in tokens:
|
|
108
|
+
if token.map:
|
|
109
|
+
token = dc.replace(
|
|
110
|
+
token,
|
|
111
|
+
map=[token.map[0] + line_offset, token.map[1] + line_offset],
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
adjusted.append(token)
|
|
115
|
+
|
|
116
|
+
return adjusted
|
omdev/markdown/tokens.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# ruff: noqa: TC002
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
with lang.auto_proxy_import(globals()):
|
|
8
|
+
import markdown_it as md
|
|
9
|
+
import markdown_it.token # noqa
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def token_repr(t: 'md.token.Token') -> str:
|
|
16
|
+
return ''.join([
|
|
17
|
+
'Token(',
|
|
18
|
+
f'type={t.type!r}',
|
|
19
|
+
*([f', tag={t.tag!r}'] if t.tag else []),
|
|
20
|
+
*([f', nesting={t.nesting!r}'] if t.nesting else []),
|
|
21
|
+
*([f', attrs={t.attrs!r}'] if t.attrs else []),
|
|
22
|
+
*([f', map={t.map!r}'] if t.map else []),
|
|
23
|
+
*([f', level={t.level!r}'] if t.level else []),
|
|
24
|
+
*([f', children={t.children!r}'] if t.children else []),
|
|
25
|
+
*([f', content={t.content!r}'] if t.content else []),
|
|
26
|
+
*([f', markup={t.markup!r}'] if t.markup else []),
|
|
27
|
+
*([f', info={t.info!r}'] if t.info else []),
|
|
28
|
+
*([f', meta={t.meta!r}'] if t.meta else []),
|
|
29
|
+
*([f', block={t.block!r}'] if t.block else []),
|
|
30
|
+
*([f', hidden={t.hidden!r}'] if t.hidden else []),
|
|
31
|
+
')',
|
|
32
|
+
])
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def flatten_tokens(
|
|
39
|
+
tokens: ta.Iterable['md.token.Token'],
|
|
40
|
+
*,
|
|
41
|
+
filter: ta.Callable[['md.token.Token'], bool] | None = None, # noqa
|
|
42
|
+
) -> ta.Iterable['md.token.Token']:
|
|
43
|
+
def rec(tks: ta.Iterable['md.token.Token']) -> ta.Iterator['md.token.Token']:
|
|
44
|
+
for tk in tks:
|
|
45
|
+
if tk.children and not (filter is not None and not filter(tk)):
|
|
46
|
+
yield from rec(tk.children)
|
|
47
|
+
|
|
48
|
+
else:
|
|
49
|
+
yield tk
|
|
50
|
+
|
|
51
|
+
return rec(tokens)
|
omdev/precheck/imports.py
CHANGED
|
@@ -3,6 +3,8 @@ import dataclasses as dc
|
|
|
3
3
|
import os.path
|
|
4
4
|
import typing as ta
|
|
5
5
|
|
|
6
|
+
from omlish.text.filecache import TextFileCache
|
|
7
|
+
|
|
6
8
|
from .base import Precheck
|
|
7
9
|
from .base import PrecheckContext
|
|
8
10
|
from .caches import AstCache
|
|
@@ -26,6 +28,7 @@ class RootRelativeImportPrecheck(Precheck['RootRelativeImportPrecheck.Config']):
|
|
|
26
28
|
dir_walk_cache: DirWalkCache,
|
|
27
29
|
headers_cache: HeadersCache,
|
|
28
30
|
ast_cache: AstCache,
|
|
31
|
+
text_file_cache: TextFileCache,
|
|
29
32
|
) -> None:
|
|
30
33
|
super().__init__(config)
|
|
31
34
|
|
|
@@ -34,6 +37,7 @@ class RootRelativeImportPrecheck(Precheck['RootRelativeImportPrecheck.Config']):
|
|
|
34
37
|
self._dir_walk_cache = dir_walk_cache
|
|
35
38
|
self._headers_cache = headers_cache
|
|
36
39
|
self._ast_cache = ast_cache
|
|
40
|
+
self._text_file_cache = text_file_cache
|
|
37
41
|
|
|
38
42
|
async def _run_py_file(self, py_file: str, src_root: str) -> ta.AsyncGenerator[Precheck.Violation]:
|
|
39
43
|
if isinstance(header_lines := self._headers_cache.get_file_headers(py_file), Exception):
|
|
@@ -41,11 +45,20 @@ class RootRelativeImportPrecheck(Precheck['RootRelativeImportPrecheck.Config']):
|
|
|
41
45
|
if any(hl.src.strip() == '# ruff: noqa' for hl in header_lines):
|
|
42
46
|
return
|
|
43
47
|
|
|
48
|
+
py_file_lines = self._text_file_cache.get_entry(py_file).lines()
|
|
49
|
+
|
|
44
50
|
py_file_ast = self._ast_cache.get_file_ast(py_file)
|
|
45
51
|
if not isinstance(py_file_ast, ast.Module):
|
|
46
52
|
return
|
|
47
53
|
|
|
48
|
-
for cur_node in py_file_ast
|
|
54
|
+
for cur_node in ast.walk(py_file_ast):
|
|
55
|
+
if not isinstance(cur_node, (ast.Import, ast.ImportFrom)):
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
# FIXME: lame lol
|
|
59
|
+
if py_file_lines[cur_node.lineno - 1].strip().endswith('# noqa'):
|
|
60
|
+
continue
|
|
61
|
+
|
|
49
62
|
if isinstance(cur_node, ast.Import):
|
|
50
63
|
imp_alias: ast.alias
|
|
51
64
|
for imp_alias in cur_node.names:
|
omdev/precheck/main.py
CHANGED
|
@@ -134,12 +134,12 @@ def _check_cmd(args) -> None:
|
|
|
134
134
|
|
|
135
135
|
def _build_parser() -> argparse.ArgumentParser:
|
|
136
136
|
parser = argparse.ArgumentParser()
|
|
137
|
+
parser.add_argument('-v', '--verbose', action='store_true')
|
|
137
138
|
|
|
138
139
|
subparsers = parser.add_subparsers()
|
|
139
140
|
|
|
140
141
|
parser_check = subparsers.add_parser('check')
|
|
141
142
|
parser_check.add_argument('roots', nargs='+')
|
|
142
|
-
parser_check.add_argument('-v', '--verbose', action='store_true')
|
|
143
143
|
parser_check.set_defaults(func=_check_cmd)
|
|
144
144
|
|
|
145
145
|
return parser
|
omdev/tui/apps/markdown/cli.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import sys
|
|
3
3
|
|
|
4
|
-
import rich
|
|
5
|
-
import rich.markdown
|
|
4
|
+
from ... import rich
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
##
|
|
@@ -19,8 +18,8 @@ def _main() -> None:
|
|
|
19
18
|
else:
|
|
20
19
|
src = sys.stdin.read()
|
|
21
20
|
|
|
22
|
-
console = rich.
|
|
23
|
-
markdown = rich.
|
|
21
|
+
console = rich.Console()
|
|
22
|
+
markdown = rich.Markdown(src)
|
|
24
23
|
console.print(markdown)
|
|
25
24
|
print()
|
|
26
25
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# ruff: noqa: F401
|
|
2
|
+
# flake8: noqa: F401
|
|
3
|
+
from omlish import lang as _lang
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
with _lang.auto_proxy_init(globals()):
|
|
7
|
+
##
|
|
8
|
+
|
|
9
|
+
from rich import console # noqa
|
|
10
|
+
from rich import live # noqa
|
|
11
|
+
from rich import markdown # noqa
|
|
12
|
+
from rich import text # noqa
|
|
13
|
+
from rich.console import Console # noqa
|
|
14
|
+
from rich.live import Live # noqa
|
|
15
|
+
from rich.markdown import Markdown # noqa
|
|
16
|
+
from rich.text import Text # noqa
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
from .console2 import ( # noqa
|
|
21
|
+
console_render,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from .markdown2 import ( # noqa
|
|
25
|
+
configure_markdown_parser,
|
|
26
|
+
markdown_from_tokens,
|
|
27
|
+
flatten_tokens_filter,
|
|
28
|
+
flatten_tokens,
|
|
29
|
+
|
|
30
|
+
MarkdownLiveStream,
|
|
31
|
+
NaiveMarkdownLiveStream,
|
|
32
|
+
IncrementalMarkdownLiveStream,
|
|
33
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
with lang.auto_proxy_import(globals()):
|
|
8
|
+
import rich.console
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def console_render(obj: ta.Any, **kwargs: ta.Any) -> str:
|
|
15
|
+
temp_console = rich.console.Console(
|
|
16
|
+
file=(out := io.StringIO()),
|
|
17
|
+
**kwargs,
|
|
18
|
+
)
|
|
19
|
+
temp_console.print(obj)
|
|
20
|
+
return out.getvalue()
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
from ...markdown.incparse import IncrementalMarkdownParser
|
|
7
|
+
from ...markdown.tokens import flatten_tokens as _flatten_tokens
|
|
8
|
+
from .console2 import console_render
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
with lang.auto_proxy_import(globals()):
|
|
12
|
+
import markdown_it as md # noqa
|
|
13
|
+
import markdown_it.token # noqa
|
|
14
|
+
import rich.console
|
|
15
|
+
import rich.live
|
|
16
|
+
import rich.markdown
|
|
17
|
+
import rich.text
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def configure_markdown_parser(parser: ta.Optional['md.MarkdownIt'] = None) -> 'md.MarkdownIt':
|
|
24
|
+
if parser is None:
|
|
25
|
+
parser = md.MarkdownIt()
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
parser
|
|
29
|
+
.enable('strikethrough')
|
|
30
|
+
.enable('table')
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def markdown_from_tokens(tokens: ta.Sequence['md.token.Token']) -> 'rich.markdown.Markdown':
|
|
35
|
+
rmd = rich.markdown.Markdown('')
|
|
36
|
+
rmd.parsed = tokens # type: ignore[assignment]
|
|
37
|
+
return rmd
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def flatten_tokens_filter(token: 'md.token.Token') -> bool:
|
|
41
|
+
return (
|
|
42
|
+
token.type != 'fence' and
|
|
43
|
+
token.tag != 'img'
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def flatten_tokens(tokens: ta.Iterable['md.token.Token']) -> ta.Iterable['md.token.Token']:
|
|
48
|
+
return _flatten_tokens(tokens, filter=flatten_tokens_filter)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class MarkdownLiveStream(lang.ExitStacked, lang.Abstract):
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
*,
|
|
58
|
+
parser: ta.Optional['md.MarkdownIt'] = None,
|
|
59
|
+
console: ta.Optional['rich.console.Console'] = None,
|
|
60
|
+
) -> None:
|
|
61
|
+
super().__init__()
|
|
62
|
+
|
|
63
|
+
if console is None:
|
|
64
|
+
console = rich.console.Console()
|
|
65
|
+
self._console = console
|
|
66
|
+
|
|
67
|
+
if parser is None:
|
|
68
|
+
parser = configure_markdown_parser()
|
|
69
|
+
self._parser = parser
|
|
70
|
+
|
|
71
|
+
self._lines_printed_to_scrollback = 0
|
|
72
|
+
|
|
73
|
+
_live: 'rich.live.Live' # noqa
|
|
74
|
+
|
|
75
|
+
def _enter_contexts(self) -> None:
|
|
76
|
+
super()._enter_contexts()
|
|
77
|
+
|
|
78
|
+
self._live = self._enter_context(rich.live.Live(
|
|
79
|
+
rich.text.Text(''),
|
|
80
|
+
console=self._console,
|
|
81
|
+
refresh_per_second=10,
|
|
82
|
+
))
|
|
83
|
+
|
|
84
|
+
def _console_render(self, obj: ta.Any) -> list[str]:
|
|
85
|
+
return console_render(
|
|
86
|
+
obj,
|
|
87
|
+
force_terminal=True,
|
|
88
|
+
width=self._console.width,
|
|
89
|
+
).splitlines()
|
|
90
|
+
|
|
91
|
+
def _console_render_markdown(self, src: str) -> list[str]:
|
|
92
|
+
return self._console_render(markdown_from_tokens(self._parser.parse(src)))
|
|
93
|
+
|
|
94
|
+
@abc.abstractmethod
|
|
95
|
+
def feed(self, s: str) -> None:
|
|
96
|
+
raise NotImplementedError
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class NaiveMarkdownLiveStream(MarkdownLiveStream):
|
|
100
|
+
_accumulated = ''
|
|
101
|
+
|
|
102
|
+
def feed(self, s: str) -> None:
|
|
103
|
+
self._accumulated += s
|
|
104
|
+
all_lines = self._console_render_markdown(self._accumulated)
|
|
105
|
+
|
|
106
|
+
# Calculate how many lines fit in the live window
|
|
107
|
+
available_height = self._console.height - 2
|
|
108
|
+
|
|
109
|
+
# Determine which lines overflow and need to be printed to scrollback
|
|
110
|
+
total_lines = len(all_lines)
|
|
111
|
+
if total_lines > available_height:
|
|
112
|
+
# Lines that should be in scrollback
|
|
113
|
+
lines_for_scrollback = total_lines - available_height
|
|
114
|
+
|
|
115
|
+
# Print any new lines that weren't already printed
|
|
116
|
+
if lines_for_scrollback > self._lines_printed_to_scrollback:
|
|
117
|
+
new_lines_to_print = all_lines[self._lines_printed_to_scrollback:lines_for_scrollback]
|
|
118
|
+
for line in new_lines_to_print:
|
|
119
|
+
self._live.console.print(rich.text.Text.from_ansi(line))
|
|
120
|
+
self._lines_printed_to_scrollback = lines_for_scrollback
|
|
121
|
+
|
|
122
|
+
# Show only the bottom portion in the live window
|
|
123
|
+
visible_lines = all_lines[-available_height:]
|
|
124
|
+
|
|
125
|
+
else:
|
|
126
|
+
visible_lines = all_lines
|
|
127
|
+
|
|
128
|
+
# Update the live display
|
|
129
|
+
self._live.update(rich.text.Text.from_ansi('\n'.join(visible_lines)))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class IncrementalMarkdownLiveStream(MarkdownLiveStream):
|
|
133
|
+
def __init__(
|
|
134
|
+
self,
|
|
135
|
+
*,
|
|
136
|
+
parser: ta.Optional['md.MarkdownIt'] = None,
|
|
137
|
+
console: ta.Optional['rich.console.Console'] = None,
|
|
138
|
+
) -> None:
|
|
139
|
+
super().__init__(
|
|
140
|
+
parser=parser,
|
|
141
|
+
console=console,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
self._inc_parser = IncrementalMarkdownParser(parser=self._parser)
|
|
145
|
+
|
|
146
|
+
def feed(self, s: str) -> None:
|
|
147
|
+
ip_out = self._inc_parser.feed2(s)
|
|
148
|
+
|
|
149
|
+
if ip_out.new_stable:
|
|
150
|
+
# try:
|
|
151
|
+
# srs = getattr(self, '_srs')
|
|
152
|
+
# except AttributeError:
|
|
153
|
+
# setattr(self, '_srs', srs := [])
|
|
154
|
+
# from ...markdown.tokens import token_repr, flatten_tokens
|
|
155
|
+
# srs.extend(map(token_repr, flatten_tokens(ip_out.new_stable)))
|
|
156
|
+
|
|
157
|
+
stable_lines = self._console_render(markdown_from_tokens(ip_out.new_stable))
|
|
158
|
+
stable_lines.append(' ') # FIXME: lame hack
|
|
159
|
+
self._live.console.print(rich.text.Text.from_ansi('\n'.join(stable_lines), no_wrap=True))
|
|
160
|
+
self._lines_printed_to_scrollback = max(0, self._lines_printed_to_scrollback - len(stable_lines))
|
|
161
|
+
|
|
162
|
+
unstable_lines = self._console_render(markdown_from_tokens(ip_out.unstable))
|
|
163
|
+
|
|
164
|
+
# Calculate how many lines fit in the live window
|
|
165
|
+
available_height = self._console.height - 2
|
|
166
|
+
|
|
167
|
+
# Determine which lines overflow and need to be printed to scrollback
|
|
168
|
+
total_lines = len(unstable_lines)
|
|
169
|
+
if total_lines > available_height:
|
|
170
|
+
# Lines that should be in scrollback
|
|
171
|
+
lines_for_scrollback = total_lines - available_height
|
|
172
|
+
|
|
173
|
+
# Print any new lines that weren't already printed
|
|
174
|
+
if lines_for_scrollback > self._lines_printed_to_scrollback:
|
|
175
|
+
new_lines_to_print = unstable_lines[self._lines_printed_to_scrollback:lines_for_scrollback]
|
|
176
|
+
self._live.console.print(rich.text.Text.from_ansi('\n'.join(new_lines_to_print)))
|
|
177
|
+
self._lines_printed_to_scrollback = lines_for_scrollback
|
|
178
|
+
|
|
179
|
+
# Show only the bottom portion in the live window
|
|
180
|
+
visible_lines = unstable_lines[-available_height:]
|
|
181
|
+
|
|
182
|
+
else:
|
|
183
|
+
visible_lines = unstable_lines
|
|
184
|
+
|
|
185
|
+
# Update the live display
|
|
186
|
+
self._live.update(rich.text.Text.from_ansi('\n'.join(visible_lines)))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omdev
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev461
|
|
4
4
|
Summary: omdev
|
|
5
5
|
Author: wrmsr
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
14
14
|
Requires-Python: >=3.13
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: omlish==0.0.0.
|
|
17
|
+
Requires-Dist: omlish==0.0.0.dev461
|
|
18
18
|
Provides-Extra: all
|
|
19
19
|
Requires-Dist: black~=25.9; extra == "all"
|
|
20
20
|
Requires-Dist: pycparser~=2.23; extra == "all"
|
|
@@ -27,7 +27,7 @@ Requires-Dist: mypy~=1.18; extra == "all"
|
|
|
27
27
|
Requires-Dist: gprof2dot~=2025.4; extra == "all"
|
|
28
28
|
Requires-Dist: segno~=1.6; extra == "all"
|
|
29
29
|
Requires-Dist: rich~=14.2; extra == "all"
|
|
30
|
-
Requires-Dist: textual~=6.
|
|
30
|
+
Requires-Dist: textual~=6.3; extra == "all"
|
|
31
31
|
Provides-Extra: black
|
|
32
32
|
Requires-Dist: black~=25.9; extra == "black"
|
|
33
33
|
Provides-Extra: c
|
|
@@ -46,7 +46,7 @@ Provides-Extra: qr
|
|
|
46
46
|
Requires-Dist: segno~=1.6; extra == "qr"
|
|
47
47
|
Provides-Extra: tui
|
|
48
48
|
Requires-Dist: rich~=14.2; extra == "tui"
|
|
49
|
-
Requires-Dist: textual~=6.
|
|
49
|
+
Requires-Dist: textual~=6.3; extra == "tui"
|
|
50
50
|
Dynamic: license-file
|
|
51
51
|
|
|
52
52
|
# Overview
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
omdev/.omlish-manifests.json,sha256=YqhbZs4Wr3L9DAwcFfhTs8Zw8W8MxPLaxYXp2aqbyzQ,11671
|
|
2
|
-
omdev/__about__.py,sha256=
|
|
2
|
+
omdev/__about__.py,sha256=TwPTq6a1_K0K-oDBQf6x2VmkiD5hi6W8Q7CvmTW9Aic,1223
|
|
3
3
|
omdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
omdev/cmake.py,sha256=gu49t10_syXh_TUJs4POsxeFs8we8Y3XTOOPgIXmGvg,4608
|
|
5
5
|
omdev/imgur.py,sha256=oqei705LhSnLWQTOMHMHwRecRXcpSEP90Sg4SVINPQ0,3133
|
|
@@ -175,6 +175,26 @@ omdev/interp/uv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
175
175
|
omdev/interp/uv/inject.py,sha256=nNCfjD-nrErM8wM__Z51rxqZjV3Cn0kE25jFAbkrPag,595
|
|
176
176
|
omdev/interp/uv/provider.py,sha256=rMrlNRrV0FxFJV0ldJJCkrUzKr--WSbUGFdMwKpFV9c,1045
|
|
177
177
|
omdev/interp/uv/uv.py,sha256=NaVQOzgbcrx6LkpXPmcmR9iVknFHgnslMlbBHf8VTTc,1810
|
|
178
|
+
omdev/irc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
179
|
+
omdev/irc/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
180
|
+
omdev/irc/messages/base.py,sha256=7tHICphzOievcjs39tzeooTL0Z6iL4nvVHxgRKK54LY,1248
|
|
181
|
+
omdev/irc/messages/formats.py,sha256=8Cz2mmlx0--Vnp4VvUYSndYc1Dnq97Mls5ol4zgdG2Q,2370
|
|
182
|
+
omdev/irc/messages/messages.py,sha256=QaNktXScvtKCWD6KbHIx_5XvXmsJlbkoVR_zXjLdyiA,14079
|
|
183
|
+
omdev/irc/messages/parsing.py,sha256=nennTwdNbaWYx-BzMe9qKhdFdTlBeEuOVrBlhMNBYf0,2225
|
|
184
|
+
omdev/irc/numerics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
185
|
+
omdev/irc/numerics/formats.py,sha256=NI5DKyePCEnOeE66R1qVgu2rUt56id_n52wzx_9w8uw,2261
|
|
186
|
+
omdev/irc/numerics/numerics.py,sha256=iOEG9PqfGUiDh6gDgdiNUqVY6WAbMBIwEPPIma34JNo,18215
|
|
187
|
+
omdev/irc/numerics/types.py,sha256=myjN4K29QzwE3XvKeyW7QYhPW-H13jCR2gvv59_g0Pc,1289
|
|
188
|
+
omdev/irc/protocol/LICENSE,sha256=1CT5_XkDMJ9HDZh0tX4TIsm7LfrSdljRX5EirPBhDYM,778
|
|
189
|
+
omdev/irc/protocol/__init__.py,sha256=0OFSLrMS5jYFJCqH1LKv0UfTrp5faPQhlhYKBQyCZeA,1701
|
|
190
|
+
omdev/irc/protocol/consts.py,sha256=iDCsJIyDE5-UnkIQpwCLzdbf3DUFWRmqkuVd0cZtujw,185
|
|
191
|
+
omdev/irc/protocol/errors.py,sha256=cRP8c--kRts5Uwqn1xl1J97OI74hicG4UbkJs_EOSDg,334
|
|
192
|
+
omdev/irc/protocol/message.py,sha256=i1-iZj17_mrmX5WaIcBuVD-dZP6g-bP6I_zH-0gVLzU,403
|
|
193
|
+
omdev/irc/protocol/nuh.py,sha256=6DamgLNVaZRZdvpGqOn-cFsnru-a_kYmJIrb_Epu8_o,1287
|
|
194
|
+
omdev/irc/protocol/parsing.py,sha256=yL9LRLw4GqtmiXmrH_AcIzu5x2riP6QDMEWESZVJ3jc,4058
|
|
195
|
+
omdev/irc/protocol/rendering.py,sha256=-V7_Yr9m_izcUrwkOeD3L4aCNTT4iLi22QH5J0Upd5g,4914
|
|
196
|
+
omdev/irc/protocol/tags.py,sha256=M89vR2GgDNroEsiH2QNchVX2LmT6sMIXY7huTEv7rIM,2784
|
|
197
|
+
omdev/irc/protocol/utils.py,sha256=tUbkShJov8oCsbg1KZE1EC5pCThXJTdXFcw0HBM5ApQ,692
|
|
178
198
|
omdev/magic/__init__.py,sha256=CBzRB71RLyylkrj8dph6JUEddA8KSMJvDgriHqFfJGU,478
|
|
179
199
|
omdev/magic/__main__.py,sha256=1_BAKDtA6Rn5hswyl4S5J78BPRbynX4is_wQsD0U7jI,161
|
|
180
200
|
omdev/magic/cli.py,sha256=DL4V34hSh0yVEtPyl6C7RSYd_td9FOT9Zn7kkyQZhvU,1190
|
|
@@ -188,6 +208,9 @@ omdev/manifests/_dumping.py,sha256=Y2zGFyRUwqqMHAvPpOsrHJhvlAz3oRl3Y7uqIT5J_Cg,5
|
|
|
188
208
|
omdev/manifests/building.py,sha256=M3IHQljk0ca0J32-xNTOcFVvW07s_7fHQ7sGsCJeurU,13908
|
|
189
209
|
omdev/manifests/dumping.py,sha256=WUIZDvOyO25AhnCPn5Nxj2OkMcZa1LRjGuCnpyx8AL8,4506
|
|
190
210
|
omdev/manifests/main.py,sha256=mYb8iM5bdwaO8jSd9_hIBSoYLf2h7e0iLb9aCCbgJ6c,2175
|
|
211
|
+
omdev/markdown/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
212
|
+
omdev/markdown/incparse.py,sha256=zldzPESdrgaoIjBLL9ZPZY11riXCY_3Ti4YDeLtz24Y,3824
|
|
213
|
+
omdev/markdown/tokens.py,sha256=YwFUhxaun4_BiKYXrrX3CBka4ruqdh8Ogl7G8hGvlLA,1483
|
|
191
214
|
omdev/mypy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
192
215
|
omdev/mypy/debug.py,sha256=VskRcr9trNhyPG2ErZZ7IX_v1DLKTLBOjp34o-fEWaM,3294
|
|
193
216
|
omdev/mypy/report.py,sha256=faX1GmbX_irmZptsOZN2HJnmHxxnrqxScmPdl7B3BI4,1570
|
|
@@ -219,9 +242,9 @@ omdev/precheck/base.py,sha256=fKdrfakq2u1UU1_JZFnl-non9bIAZMsSkVY1SMYn8xQ,662
|
|
|
219
242
|
omdev/precheck/blanklines.py,sha256=VG06NTcFoJjUBn46OXlx4qf5u3vx5l02XiNANBeuQBY,1882
|
|
220
243
|
omdev/precheck/caches.py,sha256=OZKP20DIj6OpUzdNwrjCufv1GzndEbsc7tLD-qHNv9g,1736
|
|
221
244
|
omdev/precheck/git.py,sha256=O8rNQZ_vlHec0pOFbK6LOkbly5ZIUYT_HXRMqQX8GaI,774
|
|
222
|
-
omdev/precheck/imports.py,sha256=
|
|
245
|
+
omdev/precheck/imports.py,sha256=KXNFCqDqBRUzRs1LFvKVSNDhoVWtNibFbmcqGPrqx9c,3016
|
|
223
246
|
omdev/precheck/lite.py,sha256=sseaKHMZgMhIEuifZvPJm0-wuRqRUrnyySJfHBMItOM,4737
|
|
224
|
-
omdev/precheck/main.py,sha256=
|
|
247
|
+
omdev/precheck/main.py,sha256=wwghmR8S17GpC5Jil3yg96LsIGUKH29HS0h2d4YZ8f0,4439
|
|
225
248
|
omdev/precheck/manifests.py,sha256=dxl7GSJHKjQrR6mbwvj6j92XDGHOpxxEEQ6smJkBEe4,810
|
|
226
249
|
omdev/precheck/scripts.py,sha256=6nb_lDgyX7u9kdF_BU6ubY01q_jGk96VH9q9gpOieng,1753
|
|
227
250
|
omdev/precheck/unicode.py,sha256=vMxGb4Kqg1NhFlw1kbgsPtXOnswjREH0ZPZExrrTwCU,3260
|
|
@@ -314,10 +337,14 @@ omdev/tui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
314
337
|
omdev/tui/apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
315
338
|
omdev/tui/apps/markdown/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
316
339
|
omdev/tui/apps/markdown/__main__.py,sha256=Xy-G2-8Ymx8QMBbRzA4LoiAMZqvtC944mMjFEWd69CA,182
|
|
317
|
-
omdev/tui/apps/markdown/cli.py,sha256=
|
|
318
|
-
omdev
|
|
319
|
-
omdev
|
|
320
|
-
omdev
|
|
321
|
-
omdev
|
|
322
|
-
omdev-0.0.0.
|
|
323
|
-
omdev-0.0.0.
|
|
340
|
+
omdev/tui/apps/markdown/cli.py,sha256=K1vH7f3ZqLv4xTPluhJBEZH8nx8n42_vXIALEV07Q50,469
|
|
341
|
+
omdev/tui/rich/__init__.py,sha256=_PcNDlzl7FIa071qni4AlBAf0oXXFHUjEaPxumnuXWs,775
|
|
342
|
+
omdev/tui/rich/console2.py,sha256=BYYLbbD65If9TvfPI6qUcMQKUWJbuWwykEzPplvkf6A,342
|
|
343
|
+
omdev/tui/rich/markdown2.py,sha256=fBcjG_34XzUf4WclBL_MxvBj5NUwvLCANhHCx3R0akw,6139
|
|
344
|
+
omdev/tui/textual/__init__.py,sha256=SljlE7NeWxHrWG7HyGlrtA0dTuIabXi2KidA8X9Nk5s,131
|
|
345
|
+
omdev-0.0.0.dev461.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
|
346
|
+
omdev-0.0.0.dev461.dist-info/METADATA,sha256=0xextQLtZS5bwFwkQzBFbe1FN2PXm8URpKlDFKlnCMk,5170
|
|
347
|
+
omdev-0.0.0.dev461.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
348
|
+
omdev-0.0.0.dev461.dist-info/entry_points.txt,sha256=dHLXFmq5D9B8qUyhRtFqTGWGxlbx3t5ejedjrnXNYLU,33
|
|
349
|
+
omdev-0.0.0.dev461.dist-info/top_level.txt,sha256=1nr7j30fEWgLYHW3lGR9pkdHkb7knv1U1ES1XRNVQ6k,6
|
|
350
|
+
omdev-0.0.0.dev461.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|