omdev 0.0.0.dev460__py3-none-any.whl → 0.0.0.dev462__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/.omlish-manifests.json +1 -1
- omdev/cexts/cmake.py +4 -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/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 +156 -0
- omdev/tui/textual/drivers2.py +55 -0
- {omdev-0.0.0.dev460.dist-info → omdev-0.0.0.dev462.dist-info}/METADATA +2 -2
- {omdev-0.0.0.dev460.dist-info → omdev-0.0.0.dev462.dist-info}/RECORD +37 -9
- {omdev-0.0.0.dev460.dist-info → omdev-0.0.0.dev462.dist-info}/WHEEL +0 -0
- {omdev-0.0.0.dev460.dist-info → omdev-0.0.0.dev462.dist-info}/entry_points.txt +0 -0
- {omdev-0.0.0.dev460.dist-info → omdev-0.0.0.dev462.dist-info}/licenses/LICENSE +0 -0
- {omdev-0.0.0.dev460.dist-info → omdev-0.0.0.dev462.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/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)))
|
|
@@ -0,0 +1,156 @@
|
|
|
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 textual import app # noqa
|
|
10
|
+
from textual import binding # noqa
|
|
11
|
+
from textual import containers # noqa
|
|
12
|
+
from textual import events # noqa
|
|
13
|
+
from textual import events # noqa
|
|
14
|
+
from textual import message # noqa
|
|
15
|
+
from textual import reactive # noqa
|
|
16
|
+
from textual import widgets # noqa
|
|
17
|
+
from textual.app import ActionError # noqa
|
|
18
|
+
from textual.app import ActiveModeError # noqa
|
|
19
|
+
from textual.app import App # noqa
|
|
20
|
+
from textual.app import AppError # noqa
|
|
21
|
+
from textual.app import AutopilotCallbackType # noqa
|
|
22
|
+
from textual.app import CallThreadReturnType # noqa
|
|
23
|
+
from textual.app import CommandCallback # noqa
|
|
24
|
+
from textual.app import ComposeResult # noqa
|
|
25
|
+
from textual.app import InvalidModeError # noqa
|
|
26
|
+
from textual.app import InvalidThemeError # noqa
|
|
27
|
+
from textual.app import ModeError # noqa
|
|
28
|
+
from textual.app import RenderResult # noqa
|
|
29
|
+
from textual.app import ReturnType # noqa
|
|
30
|
+
from textual.app import ScreenError # noqa
|
|
31
|
+
from textual.app import ScreenStackError # noqa
|
|
32
|
+
from textual.app import ScreenType # noqa
|
|
33
|
+
from textual.app import SuspendNotSupported # noqa
|
|
34
|
+
from textual.app import SystemCommand # noqa
|
|
35
|
+
from textual.app import UnknownModeError # noqa
|
|
36
|
+
from textual.app import get_system_commands_provider # noqa
|
|
37
|
+
from textual.binding import ActiveBinding # noqa
|
|
38
|
+
from textual.binding import Binding # noqa
|
|
39
|
+
from textual.binding import BindingError # noqa
|
|
40
|
+
from textual.binding import BindingIDString # noqa
|
|
41
|
+
from textual.binding import BindingType # noqa
|
|
42
|
+
from textual.binding import BindingsMap # noqa
|
|
43
|
+
from textual.binding import InvalidBinding # noqa
|
|
44
|
+
from textual.binding import KeyString # noqa
|
|
45
|
+
from textual.binding import Keymap # noqa
|
|
46
|
+
from textual.binding import KeymapApplyResult # noqa
|
|
47
|
+
from textual.binding import NoBinding # noqa
|
|
48
|
+
from textual.containers import Center # noqa
|
|
49
|
+
from textual.containers import CenterMiddle # noqa
|
|
50
|
+
from textual.containers import Container # noqa
|
|
51
|
+
from textual.containers import Grid # noqa
|
|
52
|
+
from textual.containers import Horizontal # noqa
|
|
53
|
+
from textual.containers import HorizontalGroup # noqa
|
|
54
|
+
from textual.containers import HorizontalScroll # noqa
|
|
55
|
+
from textual.containers import ItemGrid # noqa
|
|
56
|
+
from textual.containers import Middle # noqa
|
|
57
|
+
from textual.containers import Right # noqa
|
|
58
|
+
from textual.containers import ScrollableContainer # noqa
|
|
59
|
+
from textual.containers import Vertical # noqa
|
|
60
|
+
from textual.containers import VerticalGroup # noqa
|
|
61
|
+
from textual.containers import VerticalScroll # noqa
|
|
62
|
+
from textual.driver import Driver # noqa
|
|
63
|
+
from textual.events import Action # noqa
|
|
64
|
+
from textual.events import AppBlur # noqa
|
|
65
|
+
from textual.events import AppFocus # noqa
|
|
66
|
+
from textual.events import Blur # noqa
|
|
67
|
+
from textual.events import Callback # noqa
|
|
68
|
+
from textual.events import Click # noqa
|
|
69
|
+
from textual.events import Compose # noqa
|
|
70
|
+
from textual.events import CursorPosition # noqa
|
|
71
|
+
from textual.events import DeliveryComplete # noqa
|
|
72
|
+
from textual.events import DeliveryFailed # noqa
|
|
73
|
+
from textual.events import DescendantBlur # noqa
|
|
74
|
+
from textual.events import DescendantFocus # noqa
|
|
75
|
+
from textual.events import Enter # noqa
|
|
76
|
+
from textual.events import Event # noqa
|
|
77
|
+
from textual.events import Focus # noqa
|
|
78
|
+
from textual.events import Hide # noqa
|
|
79
|
+
from textual.events import Idle # noqa
|
|
80
|
+
from textual.events import InputEvent # noqa
|
|
81
|
+
from textual.events import Key # noqa
|
|
82
|
+
from textual.events import Leave # noqa
|
|
83
|
+
from textual.events import Load # noqa
|
|
84
|
+
from textual.events import Mount # noqa
|
|
85
|
+
from textual.events import MouseCapture # noqa
|
|
86
|
+
from textual.events import MouseDown # noqa
|
|
87
|
+
from textual.events import MouseEvent # noqa
|
|
88
|
+
from textual.events import MouseMove # noqa
|
|
89
|
+
from textual.events import MouseRelease # noqa
|
|
90
|
+
from textual.events import MouseScrollDown # noqa
|
|
91
|
+
from textual.events import MouseScrollLeft # noqa
|
|
92
|
+
from textual.events import MouseScrollRight # noqa
|
|
93
|
+
from textual.events import MouseScrollUp # noqa
|
|
94
|
+
from textual.events import MouseUp # noqa
|
|
95
|
+
from textual.events import Paste # noqa
|
|
96
|
+
from textual.events import Print # noqa
|
|
97
|
+
from textual.events import Ready # noqa
|
|
98
|
+
from textual.events import Resize # noqa
|
|
99
|
+
from textual.events import ScreenResume # noqa
|
|
100
|
+
from textual.events import ScreenSuspend # noqa
|
|
101
|
+
from textual.events import Show # noqa
|
|
102
|
+
from textual.events import Timer # noqa
|
|
103
|
+
from textual.events import Unmount # noqa
|
|
104
|
+
from textual.message import Message # noqa
|
|
105
|
+
from textual.reactive import Reactive # noqa
|
|
106
|
+
from textual.widget import Widget # noqa
|
|
107
|
+
from textual.widgets import Button # noqa
|
|
108
|
+
from textual.widgets import Checkbox # noqa
|
|
109
|
+
from textual.widgets import Collapsible # noqa
|
|
110
|
+
from textual.widgets import CollapsibleTitle # noqa
|
|
111
|
+
from textual.widgets import ContentSwitcher # noqa
|
|
112
|
+
from textual.widgets import DataTable # noqa
|
|
113
|
+
from textual.widgets import Digits # noqa
|
|
114
|
+
from textual.widgets import DirectoryTree # noqa
|
|
115
|
+
from textual.widgets import Footer # noqa
|
|
116
|
+
from textual.widgets import Header # noqa
|
|
117
|
+
from textual.widgets import HelpPanel # noqa
|
|
118
|
+
from textual.widgets import Input # noqa
|
|
119
|
+
from textual.widgets import KeyPanel # noqa
|
|
120
|
+
from textual.widgets import Label # noqa
|
|
121
|
+
from textual.widgets import Link # noqa
|
|
122
|
+
from textual.widgets import ListItem # noqa
|
|
123
|
+
from textual.widgets import ListView # noqa
|
|
124
|
+
from textual.widgets import LoadingIndicator # noqa
|
|
125
|
+
from textual.widgets import Log # noqa
|
|
126
|
+
from textual.widgets import Markdown # noqa
|
|
127
|
+
from textual.widgets import MarkdownViewer # noqa
|
|
128
|
+
from textual.widgets import MaskedInput # noqa
|
|
129
|
+
from textual.widgets import OptionList # noqa
|
|
130
|
+
from textual.widgets import Placeholder # noqa
|
|
131
|
+
from textual.widgets import Pretty # noqa
|
|
132
|
+
from textual.widgets import ProgressBar # noqa
|
|
133
|
+
from textual.widgets import RadioButton # noqa
|
|
134
|
+
from textual.widgets import RadioSet # noqa
|
|
135
|
+
from textual.widgets import RichLog # noqa
|
|
136
|
+
from textual.widgets import Rule # noqa
|
|
137
|
+
from textual.widgets import Select # noqa
|
|
138
|
+
from textual.widgets import SelectionList # noqa
|
|
139
|
+
from textual.widgets import Sparkline # noqa
|
|
140
|
+
from textual.widgets import Static # noqa
|
|
141
|
+
from textual.widgets import Switch # noqa
|
|
142
|
+
from textual.widgets import Tab # noqa
|
|
143
|
+
from textual.widgets import TabPane # noqa
|
|
144
|
+
from textual.widgets import TabbedContent # noqa
|
|
145
|
+
from textual.widgets import Tabs # noqa
|
|
146
|
+
from textual.widgets import TextArea # noqa
|
|
147
|
+
from textual.widgets import Tooltip # noqa
|
|
148
|
+
from textual.widgets import Tree # noqa
|
|
149
|
+
from textual.widgets import Welcome # noqa
|
|
150
|
+
|
|
151
|
+
##
|
|
152
|
+
|
|
153
|
+
from .drivers2 import ( # noqa
|
|
154
|
+
PendingWritesDriverMixin,
|
|
155
|
+
get_pending_writes_driver_class,
|
|
156
|
+
)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
if ta.TYPE_CHECKING:
|
|
8
|
+
from textual.driver import Driver
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PendingWritesDriverMixin:
|
|
15
|
+
def __init__(self, *args: ta.Any, **kwargs: ta.Any) -> None:
|
|
16
|
+
super().__init__(*args, **kwargs)
|
|
17
|
+
|
|
18
|
+
self._pending_primary_buffer_writes: list[str] = []
|
|
19
|
+
|
|
20
|
+
def queue_primary_buffer_write(self, *s: str) -> None:
|
|
21
|
+
self._pending_primary_buffer_writes.extend(s)
|
|
22
|
+
|
|
23
|
+
def write(self, data: str) -> None:
|
|
24
|
+
if (pw := self._pending_primary_buffer_writes):
|
|
25
|
+
data = ''.join([*pw, data])
|
|
26
|
+
pw.clear()
|
|
27
|
+
super().write(data) # type: ignore
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
_PENDING_WRITES_DRIVER_CLASSES_LOCK = threading.RLock()
|
|
31
|
+
_PENDING_WRITES_DRIVER_CLASSES: dict[type['Driver'], type['Driver']] = {}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_pending_writes_driver_class(cls: type['Driver']) -> type['Driver']:
|
|
35
|
+
if issubclass(cls, PendingWritesDriverMixin):
|
|
36
|
+
return cls # noqa
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
return _PENDING_WRITES_DRIVER_CLASSES[cls]
|
|
40
|
+
except KeyError:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
with _PENDING_WRITES_DRIVER_CLASSES_LOCK:
|
|
44
|
+
try:
|
|
45
|
+
return _PENDING_WRITES_DRIVER_CLASSES[cls]
|
|
46
|
+
except KeyError:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
cls = _PENDING_WRITES_DRIVER_CLASSES[cls] = lang.new_type( # noqa
|
|
50
|
+
f'PendingWrites{cls.__name__}',
|
|
51
|
+
(PendingWritesDriverMixin, cls),
|
|
52
|
+
{},
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return cls # noqa
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omdev
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev462
|
|
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.dev462
|
|
18
18
|
Provides-Extra: all
|
|
19
19
|
Requires-Dist: black~=25.9; extra == "all"
|
|
20
20
|
Requires-Dist: pycparser~=2.23; extra == "all"
|